diff --git a/TMessagesProj/build.gradle b/TMessagesProj/build.gradle index 7aa13b57d..13f028064 100644 --- a/TMessagesProj/build.gradle +++ b/TMessagesProj/build.gradle @@ -3,7 +3,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:1.1.3' + classpath 'com.android.tools.build:gradle:1.2.3' } } apply plugin: 'com.android.application' @@ -13,11 +13,10 @@ repositories { } dependencies { - compile 'com.android.support:support-v4:22.0.+' + compile 'com.android.support:support-v4:22.1.+' compile 'com.google.android.gms:play-services:3.2.+' compile 'net.hockeyapp.android:HockeySDK:3.5.+' compile 'com.googlecode.mp4parser:isoparser:1.0.+' - compile 'com.android.support:recyclerview-v7:+' } android { @@ -82,7 +81,7 @@ android { defaultConfig { minSdkVersion 8 targetSdkVersion 22 - versionCode 492 - versionName "2.7.0" + versionCode 542 + versionName "2.9.1" } } diff --git a/TMessagesProj/jni/Android.mk b/TMessagesProj/jni/Android.mk index f9b84ba96..19183707d 100755 --- a/TMessagesProj/jni/Android.mk +++ b/TMessagesProj/jni/Android.mk @@ -104,7 +104,7 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_PRELINK_MODULE := false LOCAL_STATIC_LIBRARIES := webp sqlite -LOCAL_MODULE := tmessages.7 +LOCAL_MODULE := tmessages.8 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 -ffast-math diff --git a/TMessagesProj/jni/image.c b/TMessagesProj/jni/image.c index 85a859007..a3a0fb862 100644 --- a/TMessagesProj/jni/image.c +++ b/TMessagesProj/jni/image.c @@ -288,7 +288,7 @@ METHODDEF(void) my_error_exit(j_common_ptr cinfo) { longjmp(myerr->setjmp_buffer, 1); } -JNIEXPORT void Java_org_telegram_messenger_Utilities_blurBitmap(JNIEnv *env, jclass class, jobject bitmap, int radius) { +JNIEXPORT void Java_org_telegram_messenger_Utilities_blurBitmap(JNIEnv *env, jclass class, jobject bitmap, int radius, int unpin) { if (!bitmap) { return; } @@ -312,7 +312,9 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_blurBitmap(JNIEnv *env, jcl } else { fastBlurMore(info.width, info.height, info.stride, pixels, radius); } - AndroidBitmap_unlockPixels(env, bitmap); + if (unpin) { + AndroidBitmap_unlockPixels(env, bitmap); + } } JNIEXPORT void Java_org_telegram_messenger_Utilities_calcCDT(JNIEnv *env, jclass class, jobject hsvBuffer, int width, int height, jobject buffer) { diff --git a/TMessagesProj/jni/sqlite/sqlite3.c b/TMessagesProj/jni/sqlite/sqlite3.c index cae0c4ad2..a09cf7178 100644 --- a/TMessagesProj/jni/sqlite/sqlite3.c +++ b/TMessagesProj/jni/sqlite/sqlite3.c @@ -1,6 +1,6 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.8.8.1. By combining all the individual C code files into this +** version 3.8.10. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements @@ -22,9 +22,6 @@ #ifndef SQLITE_PRIVATE # define SQLITE_PRIVATE static #endif -#ifndef SQLITE_API -# define SQLITE_API -#endif /************** Begin file sqliteInt.h ***************************************/ /* ** 2001 September 15 @@ -73,6 +70,7 @@ #pragma warning(disable : 4055) #pragma warning(disable : 4100) #pragma warning(disable : 4127) +#pragma warning(disable : 4130) #pragma warning(disable : 4152) #pragma warning(disable : 4189) #pragma warning(disable : 4206) @@ -90,6 +88,44 @@ /************** End of msvc.h ************************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ +/* +** Special setup for VxWorks +*/ +/************** Include vxworks.h in the middle of sqliteInt.h ***************/ +/************** Begin file vxworks.h *****************************************/ +/* +** 2015-03-02 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This file contains code that is specific to Wind River's VxWorks +*/ +#if defined(__RTP__) || defined(_WRS_KERNEL) +/* This is VxWorks. Set up things specially for that OS +*/ +#include +#include /* amalgamator: dontcache */ +#define OS_VXWORKS 1 +#define SQLITE_OS_OTHER 0 +#define SQLITE_HOMEGROWN_RECURSIVE_MUTEX 1 +#define SQLITE_OMIT_LOAD_EXTENSION 1 +#define SQLITE_ENABLE_LOCKING_STYLE 0 +#define HAVE_UTIME 1 +#else +/* This is not VxWorks. */ +#define OS_VXWORKS 0 +#endif /* defined(_WRS_KERNEL) */ + +/************** End of vxworks.h *********************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ + /* ** These #defines should enable >2GB file support on POSIX if the ** underlying operating system supports it. If the OS lacks @@ -214,16 +250,20 @@ extern "C" { /* -** Add the ability to override 'extern' +** Provide the ability to override linkage features of the interface. */ #ifndef SQLITE_EXTERN # define SQLITE_EXTERN extern #endif - #ifndef SQLITE_API # define SQLITE_API #endif - +#ifndef SQLITE_CDECL +# define SQLITE_CDECL +#endif +#ifndef SQLITE_STDCALL +# define SQLITE_STDCALL +#endif /* ** These no-op macros are used in front of interfaces to mark those @@ -278,9 +318,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.8.8.1" -#define SQLITE_VERSION_NUMBER 3008008 -#define SQLITE_SOURCE_ID "2015-01-20 16:51:25 f73337e3e289915a76ca96e7a05a1a8d4e890d55" +#define SQLITE_VERSION "3.8.10" +#define SQLITE_VERSION_NUMBER 3008010 +#define SQLITE_SOURCE_ID "2015-05-07 11:53:08 cf975957b9ae671f34bb65f049acf351e650d437" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -313,9 +353,9 @@ extern "C" { ** See also: [sqlite_version()] and [sqlite_source_id()]. */ SQLITE_API const char sqlite3_version[] = SQLITE_VERSION; -SQLITE_API const char *sqlite3_libversion(void); -SQLITE_API const char *sqlite3_sourceid(void); -SQLITE_API int sqlite3_libversion_number(void); +SQLITE_API const char *SQLITE_STDCALL sqlite3_libversion(void); +SQLITE_API const char *SQLITE_STDCALL sqlite3_sourceid(void); +SQLITE_API int SQLITE_STDCALL sqlite3_libversion_number(void); /* ** CAPI3REF: Run-Time Library Compilation Options Diagnostics @@ -340,8 +380,8 @@ SQLITE_API int sqlite3_libversion_number(void); ** [sqlite_compileoption_get()] and the [compile_options pragma]. */ #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS -SQLITE_API int sqlite3_compileoption_used(const char *zOptName); -SQLITE_API const char *sqlite3_compileoption_get(int N); +SQLITE_API int SQLITE_STDCALL sqlite3_compileoption_used(const char *zOptName); +SQLITE_API const char *SQLITE_STDCALL sqlite3_compileoption_get(int N); #endif /* @@ -380,7 +420,7 @@ SQLITE_API const char *sqlite3_compileoption_get(int N); ** ** See the [threading mode] documentation for additional information. */ -SQLITE_API int sqlite3_threadsafe(void); +SQLITE_API int SQLITE_STDCALL sqlite3_threadsafe(void); /* ** CAPI3REF: Database Connection Handle @@ -437,6 +477,7 @@ typedef sqlite_uint64 sqlite3_uint64; /* ** CAPI3REF: Closing A Database Connection +** DESTRUCTOR: sqlite3 ** ** ^The sqlite3_close() and sqlite3_close_v2() routines are destructors ** for the [sqlite3] object. @@ -476,8 +517,8 @@ typedef sqlite_uint64 sqlite3_uint64; ** ^Calling sqlite3_close() or sqlite3_close_v2() with a NULL pointer ** argument is a harmless no-op. */ -SQLITE_API int sqlite3_close(sqlite3*); -SQLITE_API int sqlite3_close_v2(sqlite3*); +SQLITE_API int SQLITE_STDCALL sqlite3_close(sqlite3*); +SQLITE_API int SQLITE_STDCALL sqlite3_close_v2(sqlite3*); /* ** The type for a callback function. @@ -488,6 +529,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**); /* ** CAPI3REF: One-Step Query Execution Interface +** METHOD: sqlite3 ** ** The sqlite3_exec() interface is a convenience wrapper around ** [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()], @@ -547,7 +589,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**); ** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running. ** */ -SQLITE_API int sqlite3_exec( +SQLITE_API int SQLITE_STDCALL sqlite3_exec( sqlite3*, /* An open database */ const char *sql, /* SQL to be evaluated */ int (*callback)(void*,int,char**,char**), /* Callback function */ @@ -927,14 +969,16 @@ struct sqlite3_io_methods { ** of the [sqlite3_io_methods] object and for the [sqlite3_file_control()] ** interface. ** +** )^ ** ^Memory allocation statistics are enabled by default unless SQLite is ** compiled with [SQLITE_DEFAULT_MEMSTATUS]=0 in which case memory @@ -1912,7 +1973,6 @@ struct sqlite3_mem_methods { ** compiled for Windows with the [SQLITE_WIN32_MALLOC] pre-processor macro ** defined. ^SQLITE_CONFIG_WIN32_HEAPSIZE takes a 32-bit unsigned integer value ** that specifies the maximum size of the created heap. -** ** ** [[SQLITE_CONFIG_PCACHE_HDRSZ]] **
SQLITE_CONFIG_PCACHE_HDRSZ @@ -2025,15 +2085,17 @@ struct sqlite3_mem_methods { /* ** CAPI3REF: Enable Or Disable Extended Result Codes +** METHOD: sqlite3 ** ** ^The sqlite3_extended_result_codes() routine enables or disables the ** [extended result codes] feature of SQLite. ^The extended result ** codes are disabled by default for historical compatibility. */ -SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff); +SQLITE_API int SQLITE_STDCALL sqlite3_extended_result_codes(sqlite3*, int onoff); /* ** CAPI3REF: Last Insert Rowid +** METHOD: sqlite3 ** ** ^Each entry in most SQLite tables (except for [WITHOUT ROWID] tables) ** has a unique 64-bit signed @@ -2081,10 +2143,11 @@ SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff); ** unpredictable and might not equal either the old or the new ** last insert [rowid]. */ -SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_last_insert_rowid(sqlite3*); /* ** CAPI3REF: Count The Number Of Rows Modified +** METHOD: sqlite3 ** ** ^This function returns the number of rows modified, inserted or ** deleted by the most recently completed INSERT, UPDATE or DELETE @@ -2133,10 +2196,11 @@ SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); ** while [sqlite3_changes()] is running then the value returned ** is unpredictable and not meaningful. */ -SQLITE_API int sqlite3_changes(sqlite3*); +SQLITE_API int SQLITE_STDCALL sqlite3_changes(sqlite3*); /* ** CAPI3REF: Total Number Of Rows Modified +** METHOD: sqlite3 ** ** ^This function returns the total number of rows inserted, modified or ** deleted by all [INSERT], [UPDATE] or [DELETE] statements completed @@ -2156,10 +2220,11 @@ SQLITE_API int sqlite3_changes(sqlite3*); ** while [sqlite3_total_changes()] is running then the value ** returned is unpredictable and not meaningful. */ -SQLITE_API int sqlite3_total_changes(sqlite3*); +SQLITE_API int SQLITE_STDCALL sqlite3_total_changes(sqlite3*); /* ** CAPI3REF: Interrupt A Long-Running Query +** METHOD: sqlite3 ** ** ^This function causes any pending database operation to abort and ** return at its earliest opportunity. This routine is typically @@ -2195,7 +2260,7 @@ SQLITE_API int sqlite3_total_changes(sqlite3*); ** If the database connection closes while [sqlite3_interrupt()] ** is running then bad things will likely happen. */ -SQLITE_API void sqlite3_interrupt(sqlite3*); +SQLITE_API void SQLITE_STDCALL sqlite3_interrupt(sqlite3*); /* ** CAPI3REF: Determine If An SQL Statement Is Complete @@ -2230,12 +2295,13 @@ SQLITE_API void sqlite3_interrupt(sqlite3*); ** The input to [sqlite3_complete16()] must be a zero-terminated ** UTF-16 string in native byte order. */ -SQLITE_API int sqlite3_complete(const char *sql); -SQLITE_API int sqlite3_complete16(const void *sql); +SQLITE_API int SQLITE_STDCALL sqlite3_complete(const char *sql); +SQLITE_API int SQLITE_STDCALL sqlite3_complete16(const void *sql); /* ** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors ** KEYWORDS: {busy-handler callback} {busy handler} +** METHOD: sqlite3 ** ** ^The sqlite3_busy_handler(D,X,P) routine sets a callback function X ** that might be invoked with argument P whenever @@ -2291,10 +2357,11 @@ SQLITE_API int sqlite3_complete16(const void *sql); ** A busy handler must not close the database connection ** or [prepared statement] that invoked the busy handler. */ -SQLITE_API int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); +SQLITE_API int SQLITE_STDCALL sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); /* ** CAPI3REF: Set A Busy Timeout +** METHOD: sqlite3 ** ** ^This routine sets a [sqlite3_busy_handler | busy handler] that sleeps ** for a specified amount of time when a table is locked. ^The handler @@ -2313,10 +2380,11 @@ SQLITE_API int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); ** ** See also: [PRAGMA busy_timeout] */ -SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); +SQLITE_API int SQLITE_STDCALL sqlite3_busy_timeout(sqlite3*, int ms); /* ** CAPI3REF: Convenience Routines For Running Queries +** METHOD: sqlite3 ** ** This is a legacy interface that is preserved for backwards compatibility. ** Use of this interface is not recommended. @@ -2387,7 +2455,7 @@ SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); ** reflected in subsequent calls to [sqlite3_errcode()] or ** [sqlite3_errmsg()]. */ -SQLITE_API int sqlite3_get_table( +SQLITE_API int SQLITE_STDCALL sqlite3_get_table( sqlite3 *db, /* An open database */ const char *zSql, /* SQL to be evaluated */ char ***pazResult, /* Results of the query */ @@ -2395,13 +2463,17 @@ SQLITE_API int sqlite3_get_table( int *pnColumn, /* Number of result columns written here */ char **pzErrmsg /* Error msg written here */ ); -SQLITE_API void sqlite3_free_table(char **result); +SQLITE_API void SQLITE_STDCALL sqlite3_free_table(char **result); /* ** CAPI3REF: Formatted String Printing Functions ** ** These routines are work-alikes of the "printf()" family of functions ** from the standard C library. +** These routines understand most of the common K&R formatting options, +** plus some additional non-standard formats, detailed below. +** Note that some of the more obscure formatting options from recent +** C-library standards are omitted from this implementation. ** ** ^The sqlite3_mprintf() and sqlite3_vmprintf() routines write their ** results into memory obtained from [sqlite3_malloc()]. @@ -2434,7 +2506,7 @@ SQLITE_API void sqlite3_free_table(char **result); ** These routines all implement some additional formatting ** options that are useful for constructing SQL statements. ** All of the usual printf() formatting options apply. In addition, there -** is are "%q", "%Q", and "%z" options. +** is are "%q", "%Q", "%w" and "%z" options. ** ** ^(The %q option works like %s in that it substitutes a nul-terminated ** string from the argument list. But %q also doubles every '\'' character. @@ -2487,14 +2559,20 @@ SQLITE_API void sqlite3_free_table(char **result); ** The code above will render a correct SQL statement in the zSQL ** variable even if the zText variable is a NULL pointer. ** +** ^(The "%w" formatting option is like "%q" except that it expects to +** be contained within double-quotes instead of single quotes, and it +** escapes the double-quote character instead of the single-quote +** character.)^ The "%w" formatting option is intended for safely inserting +** table and column names into a constructed SQL statement. +** ** ^(The "%z" formatting option works like "%s" but with the ** addition that after the string has been read and copied into ** the result, [sqlite3_free()] is called on the input string.)^ */ -SQLITE_API char *sqlite3_mprintf(const char*,...); -SQLITE_API char *sqlite3_vmprintf(const char*, va_list); -SQLITE_API char *sqlite3_snprintf(int,char*,const char*, ...); -SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list); +SQLITE_API char *SQLITE_CDECL sqlite3_mprintf(const char*,...); +SQLITE_API char *SQLITE_STDCALL sqlite3_vmprintf(const char*, va_list); +SQLITE_API char *SQLITE_CDECL sqlite3_snprintf(int,char*,const char*, ...); +SQLITE_API char *SQLITE_STDCALL sqlite3_vsnprintf(int,char*,const char*, va_list); /* ** CAPI3REF: Memory Allocation Subsystem @@ -2584,12 +2662,12 @@ SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list); ** a block of memory after it has been released using ** [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*); +SQLITE_API void *SQLITE_STDCALL sqlite3_malloc(int); +SQLITE_API void *SQLITE_STDCALL sqlite3_malloc64(sqlite3_uint64); +SQLITE_API void *SQLITE_STDCALL sqlite3_realloc(void*, int); +SQLITE_API void *SQLITE_STDCALL sqlite3_realloc64(void*, sqlite3_uint64); +SQLITE_API void SQLITE_STDCALL sqlite3_free(void*); +SQLITE_API sqlite3_uint64 SQLITE_STDCALL sqlite3_msize(void*); /* ** CAPI3REF: Memory Allocator Statistics @@ -2614,8 +2692,8 @@ SQLITE_API sqlite3_uint64 sqlite3_msize(void*); ** by [sqlite3_memory_highwater(1)] is the high-water mark ** prior to the reset. */ -SQLITE_API sqlite3_int64 sqlite3_memory_used(void); -SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_memory_used(void); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_memory_highwater(int resetFlag); /* ** CAPI3REF: Pseudo-Random Number Generator @@ -2638,10 +2716,11 @@ SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag); ** internally and without recourse to the [sqlite3_vfs] xRandomness ** method. */ -SQLITE_API void sqlite3_randomness(int N, void *P); +SQLITE_API void SQLITE_STDCALL sqlite3_randomness(int N, void *P); /* ** CAPI3REF: Compile-Time Authorization Callbacks +** METHOD: sqlite3 ** ** ^This routine registers an authorizer callback with a particular ** [database connection], supplied in the first argument. @@ -2720,7 +2799,7 @@ SQLITE_API void sqlite3_randomness(int N, void *P); ** as stated in the previous paragraph, sqlite3_step() invokes ** sqlite3_prepare_v2() to reprepare a statement after a schema change. */ -SQLITE_API int sqlite3_set_authorizer( +SQLITE_API int SQLITE_STDCALL sqlite3_set_authorizer( sqlite3*, int (*xAuth)(void*,int,const char*,const char*,const char*,const char*), void *pUserData @@ -2798,6 +2877,7 @@ SQLITE_API int sqlite3_set_authorizer( /* ** CAPI3REF: Tracing And Profiling Functions +** METHOD: sqlite3 ** ** These routines register callback functions that can be used for ** tracing and profiling the execution of SQL statements. @@ -2824,12 +2904,13 @@ SQLITE_API int sqlite3_set_authorizer( ** sqlite3_profile() function is considered experimental and is ** subject to change in future versions of SQLite. */ -SQLITE_API void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); -SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*, +SQLITE_API void *SQLITE_STDCALL sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); +SQLITE_API SQLITE_EXPERIMENTAL void *SQLITE_STDCALL sqlite3_profile(sqlite3*, void(*xProfile)(void*,const char*,sqlite3_uint64), void*); /* ** CAPI3REF: Query Progress Callbacks +** METHOD: sqlite3 ** ** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback ** function X to be invoked periodically during long running calls to @@ -2859,10 +2940,11 @@ SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*, ** database connections for the meaning of "modify" in this paragraph. ** */ -SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); +SQLITE_API void SQLITE_STDCALL sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); /* ** CAPI3REF: Opening A New Database Connection +** CONSTRUCTOR: sqlite3 ** ** ^These routines open an SQLite database file as specified by the ** filename argument. ^The filename argument is interpreted as UTF-8 for @@ -3087,15 +3169,15 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** ** See also: [sqlite3_temp_directory] */ -SQLITE_API int sqlite3_open( +SQLITE_API int SQLITE_STDCALL sqlite3_open( const char *filename, /* Database filename (UTF-8) */ sqlite3 **ppDb /* OUT: SQLite db handle */ ); -SQLITE_API int sqlite3_open16( +SQLITE_API int SQLITE_STDCALL sqlite3_open16( const void *filename, /* Database filename (UTF-16) */ sqlite3 **ppDb /* OUT: SQLite db handle */ ); -SQLITE_API int sqlite3_open_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_open_v2( const char *filename, /* Database filename (UTF-8) */ sqlite3 **ppDb, /* OUT: SQLite db handle */ int flags, /* Flags */ @@ -3141,19 +3223,22 @@ SQLITE_API int sqlite3_open_v2( ** VFS method, then the behavior of this routine is undefined and probably ** undesirable. */ -SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); -SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); -SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64); +SQLITE_API const char *SQLITE_STDCALL sqlite3_uri_parameter(const char *zFilename, const char *zParam); +SQLITE_API int SQLITE_STDCALL sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_uri_int64(const char*, const char*, sqlite3_int64); /* ** CAPI3REF: Error Codes And Messages +** METHOD: sqlite3 ** -** ^The sqlite3_errcode() interface returns the numeric [result code] or -** [extended result code] for the most recent failed sqlite3_* API call -** associated with a [database connection]. If a prior API call failed -** but the most recent API call succeeded, the return value from -** sqlite3_errcode() is undefined. ^The sqlite3_extended_errcode() +** ^If the most recent sqlite3_* API call associated with +** [database connection] D failed, then the sqlite3_errcode(D) interface +** returns the numeric [result code] or [extended result code] for that +** API call. +** If the most recent API call was successful, +** then the return value from sqlite3_errcode() is undefined. +** ^The sqlite3_extended_errcode() ** interface is the same except that it always returns the ** [extended result code] even when extended result codes are ** disabled. @@ -3184,40 +3269,41 @@ SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int ** was invoked incorrectly by the application. In that case, the ** error code and message may or may not be set. */ -SQLITE_API int sqlite3_errcode(sqlite3 *db); -SQLITE_API int sqlite3_extended_errcode(sqlite3 *db); -SQLITE_API const char *sqlite3_errmsg(sqlite3*); -SQLITE_API const void *sqlite3_errmsg16(sqlite3*); -SQLITE_API const char *sqlite3_errstr(int); +SQLITE_API int SQLITE_STDCALL sqlite3_errcode(sqlite3 *db); +SQLITE_API int SQLITE_STDCALL sqlite3_extended_errcode(sqlite3 *db); +SQLITE_API const char *SQLITE_STDCALL sqlite3_errmsg(sqlite3*); +SQLITE_API const void *SQLITE_STDCALL sqlite3_errmsg16(sqlite3*); +SQLITE_API const char *SQLITE_STDCALL sqlite3_errstr(int); /* -** CAPI3REF: SQL Statement Object +** CAPI3REF: Prepared Statement Object ** KEYWORDS: {prepared statement} {prepared statements} ** -** An instance of this object represents a single SQL statement. -** This object is variously known as a "prepared statement" or a -** "compiled SQL statement" or simply as a "statement". +** An instance of this object represents a single SQL statement that +** has been compiled into binary form and is ready to be evaluated. ** -** The life of a statement object goes something like this: +** Think of each SQL statement as a separate computer program. The +** original SQL text is source code. A prepared statement object +** is the compiled object code. All SQL must be converted into a +** prepared statement before it can be run. +** +** The life-cycle of a prepared statement object usually goes like this: ** **
    -**
  1. Create the object using [sqlite3_prepare_v2()] or a related -** function. -**
  2. Bind values to [host parameters] using the sqlite3_bind_*() +**
  3. Create the prepared statement object using [sqlite3_prepare_v2()]. +**
  4. Bind values to [parameters] using the sqlite3_bind_*() ** interfaces. **
  5. Run the SQL by calling [sqlite3_step()] one or more times. -**
  6. Reset the statement using [sqlite3_reset()] then go back +**
  7. Reset the prepared statement using [sqlite3_reset()] then go back ** to step 2. Do this zero or more times. **
  8. Destroy the object using [sqlite3_finalize()]. **
-** -** Refer to documentation on individual methods above for additional -** information. */ typedef struct sqlite3_stmt sqlite3_stmt; /* ** CAPI3REF: Run-time Limits +** METHOD: sqlite3 ** ** ^(This interface allows the size of various constructs to be limited ** on a connection by connection basis. The first parameter is the @@ -3255,7 +3341,7 @@ typedef struct sqlite3_stmt sqlite3_stmt; ** ** New run-time limit categories may be added in future releases. */ -SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); +SQLITE_API int SQLITE_STDCALL sqlite3_limit(sqlite3*, int id, int newVal); /* ** CAPI3REF: Run-Time Limit Categories @@ -3329,6 +3415,8 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); /* ** CAPI3REF: Compiling An SQL Statement ** KEYWORDS: {SQL statement compiler} +** METHOD: sqlite3 +** CONSTRUCTOR: sqlite3_stmt ** ** To execute an SQL query, it must first be compiled into a byte-code ** program using one of these routines. @@ -3342,16 +3430,14 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** interfaces use UTF-8, and sqlite3_prepare16() and sqlite3_prepare16_v2() ** use UTF-16. ** -** ^If the nByte argument is less than zero, then zSql is read up to the -** first zero terminator. ^If nByte is non-negative, then it is the maximum -** number of bytes read from zSql. ^When nByte is non-negative, the -** zSql string ends at either the first '\000' or '\u0000' character or -** the nByte-th byte, whichever comes first. If the caller knows -** that the supplied string is nul-terminated, then there is a small -** performance advantage to be gained by passing an nByte parameter that -** is equal to the number of bytes in the input string including -** the nul-terminator bytes as this saves SQLite from having to -** make a copy of the input string. +** ^If the nByte argument is negative, then zSql is read up to the +** first zero terminator. ^If nByte is positive, then it is the +** number of bytes read from zSql. ^If nByte is zero, then no prepared +** statement is generated. +** If the caller knows that the supplied string is nul-terminated, then +** there is a small performance advantage to passing an nByte parameter that +** is the number of bytes in the input string including +** the nul-terminator. ** ** ^If pzTail is not NULL then *pzTail is made to point to the first byte ** past the end of the first SQL statement in zSql. These routines only @@ -3407,28 +3493,28 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** ** */ -SQLITE_API int sqlite3_prepare( +SQLITE_API int SQLITE_STDCALL sqlite3_prepare( sqlite3 *db, /* Database handle */ const char *zSql, /* SQL statement, UTF-8 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const char **pzTail /* OUT: Pointer to unused portion of zSql */ ); -SQLITE_API int sqlite3_prepare_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_prepare_v2( sqlite3 *db, /* Database handle */ const char *zSql, /* SQL statement, UTF-8 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const char **pzTail /* OUT: Pointer to unused portion of zSql */ ); -SQLITE_API int sqlite3_prepare16( +SQLITE_API int SQLITE_STDCALL sqlite3_prepare16( sqlite3 *db, /* Database handle */ const void *zSql, /* SQL statement, UTF-16 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const void **pzTail /* OUT: Pointer to unused portion of zSql */ ); -SQLITE_API int sqlite3_prepare16_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_prepare16_v2( sqlite3 *db, /* Database handle */ const void *zSql, /* SQL statement, UTF-16 encoded */ int nByte, /* Maximum length of zSql in bytes. */ @@ -3438,15 +3524,17 @@ SQLITE_API int sqlite3_prepare16_v2( /* ** CAPI3REF: Retrieving Statement SQL +** METHOD: sqlite3_stmt ** ** ^This interface can be used to retrieve a saved copy of the original ** SQL text used to create a [prepared statement] if that statement was ** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()]. */ -SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); +SQLITE_API const char *SQLITE_STDCALL sqlite3_sql(sqlite3_stmt *pStmt); /* ** CAPI3REF: Determine If An SQL Statement Writes The Database +** METHOD: sqlite3_stmt ** ** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if ** and only if the [prepared statement] X makes no direct changes to @@ -3474,10 +3562,11 @@ SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); ** change the configuration of a database connection, they do not make ** changes to the content of the database files on disk. */ -SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); +SQLITE_API int SQLITE_STDCALL sqlite3_stmt_readonly(sqlite3_stmt *pStmt); /* ** CAPI3REF: Determine If A Prepared Statement Has Been Reset +** METHOD: sqlite3_stmt ** ** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the ** [prepared statement] S has been stepped at least once using @@ -3493,7 +3582,7 @@ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); ** for example, in diagnostic routines to search for prepared ** statements that are holding a transaction open. */ -SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*); +SQLITE_API int SQLITE_STDCALL sqlite3_stmt_busy(sqlite3_stmt*); /* ** CAPI3REF: Dynamically Typed Value Object @@ -3552,6 +3641,7 @@ typedef struct sqlite3_context sqlite3_context; ** CAPI3REF: Binding Values To Prepared Statements ** KEYWORDS: {host parameter} {host parameters} {host parameter name} ** KEYWORDS: {SQL parameter} {SQL parameters} {parameter binding} +** METHOD: sqlite3_stmt ** ** ^(In the SQL statement text input to [sqlite3_prepare_v2()] and its variants, ** literals may be replaced by a [parameter] that matches one of following @@ -3654,22 +3744,23 @@ typedef struct sqlite3_context sqlite3_context; ** See also: [sqlite3_bind_parameter_count()], ** [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, +SQLITE_API int SQLITE_STDCALL sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); +SQLITE_API int SQLITE_STDCALL 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,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, +SQLITE_API int SQLITE_STDCALL sqlite3_bind_double(sqlite3_stmt*, int, double); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_int(sqlite3_stmt*, int, int); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_null(sqlite3_stmt*, int); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*)); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*)); +SQLITE_API int SQLITE_STDCALL 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); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); /* ** CAPI3REF: Number Of SQL Parameters +** METHOD: sqlite3_stmt ** ** ^This routine can be used to find the number of [SQL parameters] ** in a [prepared statement]. SQL parameters are tokens of the @@ -3686,10 +3777,11 @@ SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); ** [sqlite3_bind_parameter_name()], and ** [sqlite3_bind_parameter_index()]. */ -SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt*); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_parameter_count(sqlite3_stmt*); /* ** CAPI3REF: Name Of A Host Parameter +** METHOD: sqlite3_stmt ** ** ^The sqlite3_bind_parameter_name(P,N) interface returns ** the name of the N-th [SQL parameter] in the [prepared statement] P. @@ -3713,10 +3805,11 @@ SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt*); ** [sqlite3_bind_parameter_count()], and ** [sqlite3_bind_parameter_index()]. */ -SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); +SQLITE_API const char *SQLITE_STDCALL sqlite3_bind_parameter_name(sqlite3_stmt*, int); /* ** CAPI3REF: Index Of A Parameter With A Given Name +** METHOD: sqlite3_stmt ** ** ^Return the index of an SQL parameter given its name. ^The ** index value returned is suitable for use as the second @@ -3729,19 +3822,21 @@ SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); ** [sqlite3_bind_parameter_count()], and ** [sqlite3_bind_parameter_index()]. */ -SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); /* ** CAPI3REF: Reset All Bindings On A Prepared Statement +** METHOD: sqlite3_stmt ** ** ^Contrary to the intuition of many, [sqlite3_reset()] does not reset ** the [sqlite3_bind_blob | bindings] on a [prepared statement]. ** ^Use this routine to reset all host parameters to NULL. */ -SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*); +SQLITE_API int SQLITE_STDCALL sqlite3_clear_bindings(sqlite3_stmt*); /* ** CAPI3REF: Number Of Columns In A Result Set +** METHOD: sqlite3_stmt ** ** ^Return the number of columns in the result set returned by the ** [prepared statement]. ^This routine returns 0 if pStmt is an SQL @@ -3749,10 +3844,11 @@ SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*); ** ** See also: [sqlite3_data_count()] */ -SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt); +SQLITE_API int SQLITE_STDCALL sqlite3_column_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Column Names In A Result Set +** METHOD: sqlite3_stmt ** ** ^These routines return the name assigned to a particular column ** in the result set of a [SELECT] statement. ^The sqlite3_column_name() @@ -3777,11 +3873,12 @@ SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt); ** then the name of the column is unspecified and may change from ** one release of SQLite to the next. */ -SQLITE_API const char *sqlite3_column_name(sqlite3_stmt*, int N); -SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_name(sqlite3_stmt*, int N); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_name16(sqlite3_stmt*, int N); /* ** CAPI3REF: Source Of Data In A Query Result +** METHOD: sqlite3_stmt ** ** ^These routines provide a means to determine the database, table, and ** table column that is the origin of a particular result column in @@ -3825,15 +3922,16 @@ SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); ** for the same [prepared statement] and result column ** at the same time then the results are undefined. */ -SQLITE_API const char *sqlite3_column_database_name(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt*,int); -SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt*,int); -SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt*,int); +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_database_name(sqlite3_stmt*,int); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_database_name16(sqlite3_stmt*,int); +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_table_name(sqlite3_stmt*,int); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_table_name16(sqlite3_stmt*,int); +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_origin_name(sqlite3_stmt*,int); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_origin_name16(sqlite3_stmt*,int); /* ** CAPI3REF: Declared Datatype Of A Query Result +** METHOD: sqlite3_stmt ** ** ^(The first parameter is a [prepared statement]. ** If this statement is a [SELECT] statement and the Nth column of the @@ -3861,11 +3959,12 @@ SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt*,int); ** is associated with individual values, not with the containers ** used to hold those values. */ -SQLITE_API const char *sqlite3_column_decltype(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_decltype(sqlite3_stmt*,int); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_decltype16(sqlite3_stmt*,int); /* ** CAPI3REF: Evaluate An SQL Statement +** METHOD: sqlite3_stmt ** ** After a [prepared statement] has been prepared using either ** [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] or one of the legacy @@ -3941,10 +4040,11 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); ** then the more specific [error codes] are returned directly ** by sqlite3_step(). The use of the "v2" interface is recommended. */ -SQLITE_API int sqlite3_step(sqlite3_stmt*); +SQLITE_API int SQLITE_STDCALL sqlite3_step(sqlite3_stmt*); /* ** CAPI3REF: Number of columns in a result set +** METHOD: sqlite3_stmt ** ** ^The sqlite3_data_count(P) interface returns the number of columns in the ** current row of the result set of [prepared statement] P. @@ -3961,7 +4061,7 @@ SQLITE_API int sqlite3_step(sqlite3_stmt*); ** ** See also: [sqlite3_column_count()] */ -SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); +SQLITE_API int SQLITE_STDCALL sqlite3_data_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Fundamental Datatypes @@ -3998,6 +4098,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Result Values From A Query ** KEYWORDS: {column access functions} +** METHOD: sqlite3_stmt ** ** These routines form the "result set" interface. ** @@ -4157,19 +4258,20 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); ** pointer. Subsequent calls to [sqlite3_errcode()] will return ** [SQLITE_NOMEM].)^ */ -SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_bytes(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_bytes16(sqlite3_stmt*, int iCol); -SQLITE_API double sqlite3_column_double(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_int(sqlite3_stmt*, int iCol); -SQLITE_API sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); -SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); -SQLITE_API const void *sqlite3_column_text16(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol); -SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_blob(sqlite3_stmt*, int iCol); +SQLITE_API int SQLITE_STDCALL sqlite3_column_bytes(sqlite3_stmt*, int iCol); +SQLITE_API int SQLITE_STDCALL sqlite3_column_bytes16(sqlite3_stmt*, int iCol); +SQLITE_API double SQLITE_STDCALL sqlite3_column_double(sqlite3_stmt*, int iCol); +SQLITE_API int SQLITE_STDCALL sqlite3_column_int(sqlite3_stmt*, int iCol); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_column_int64(sqlite3_stmt*, int iCol); +SQLITE_API const unsigned char *SQLITE_STDCALL sqlite3_column_text(sqlite3_stmt*, int iCol); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_text16(sqlite3_stmt*, int iCol); +SQLITE_API int SQLITE_STDCALL sqlite3_column_type(sqlite3_stmt*, int iCol); +SQLITE_API sqlite3_value *SQLITE_STDCALL sqlite3_column_value(sqlite3_stmt*, int iCol); /* ** CAPI3REF: Destroy A Prepared Statement Object +** DESTRUCTOR: sqlite3_stmt ** ** ^The sqlite3_finalize() function is called to delete a [prepared statement]. ** ^If the most recent evaluation of the statement encountered no errors @@ -4193,10 +4295,11 @@ SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); ** statement after it has been finalized can result in undefined and ** undesirable behavior such as segfaults and heap corruption. */ -SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); +SQLITE_API int SQLITE_STDCALL sqlite3_finalize(sqlite3_stmt *pStmt); /* ** CAPI3REF: Reset A Prepared Statement Object +** METHOD: sqlite3_stmt ** ** The sqlite3_reset() function is called to reset a [prepared statement] ** object back to its initial state, ready to be re-executed. @@ -4219,13 +4322,14 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); ** ^The [sqlite3_reset(S)] interface does not change the values ** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S. */ -SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); +SQLITE_API int SQLITE_STDCALL sqlite3_reset(sqlite3_stmt *pStmt); /* ** CAPI3REF: Create Or Redefine SQL Functions ** KEYWORDS: {function creation routines} ** KEYWORDS: {application-defined SQL function} ** KEYWORDS: {application-defined SQL functions} +** METHOD: sqlite3 ** ** ^These functions (collectively known as "function creation routines") ** are used to add SQL functions or aggregates or to redefine the behavior @@ -4318,7 +4422,7 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); ** close the database connection nor finalize or reset the prepared ** statement in which the function is running. */ -SQLITE_API int sqlite3_create_function( +SQLITE_API int SQLITE_STDCALL sqlite3_create_function( sqlite3 *db, const char *zFunctionName, int nArg, @@ -4328,7 +4432,7 @@ SQLITE_API int sqlite3_create_function( void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*) ); -SQLITE_API int sqlite3_create_function16( +SQLITE_API int SQLITE_STDCALL sqlite3_create_function16( sqlite3 *db, const void *zFunctionName, int nArg, @@ -4338,7 +4442,7 @@ SQLITE_API int sqlite3_create_function16( void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*) ); -SQLITE_API int sqlite3_create_function_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_create_function_v2( sqlite3 *db, const char *zFunctionName, int nArg, @@ -4380,21 +4484,22 @@ SQLITE_API int sqlite3_create_function_v2( ** These functions are [deprecated]. In order to maintain ** backwards compatibility with older code, these functions continue ** to be supported. However, new applications should avoid -** the use of these functions. To help encourage people to avoid -** using these functions, we are not going to tell you what they do. +** the use of these functions. To encourage programmers to avoid +** these functions, we will not explain what they do. */ #ifndef SQLITE_OMIT_DEPRECATED -SQLITE_API SQLITE_DEPRECATED int sqlite3_aggregate_count(sqlite3_context*); -SQLITE_API SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*); -SQLITE_API SQLITE_DEPRECATED int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*); -SQLITE_API SQLITE_DEPRECATED int sqlite3_global_recover(void); -SQLITE_API SQLITE_DEPRECATED void sqlite3_thread_cleanup(void); -SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int), +SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_aggregate_count(sqlite3_context*); +SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_expired(sqlite3_stmt*); +SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*); +SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_global_recover(void); +SQLITE_API SQLITE_DEPRECATED void SQLITE_STDCALL sqlite3_thread_cleanup(void); +SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int), void*,sqlite3_int64); #endif /* ** CAPI3REF: Obtaining SQL Function Parameter Values +** METHOD: sqlite3_value ** ** The C-language implementation of SQL functions and aggregates uses ** this set of interface routines to access the parameter values on @@ -4438,21 +4543,22 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6 ** These routines must be called from the same thread as ** the SQL function that supplied the [sqlite3_value*] parameters. */ -SQLITE_API const void *sqlite3_value_blob(sqlite3_value*); -SQLITE_API int sqlite3_value_bytes(sqlite3_value*); -SQLITE_API int sqlite3_value_bytes16(sqlite3_value*); -SQLITE_API double sqlite3_value_double(sqlite3_value*); -SQLITE_API int sqlite3_value_int(sqlite3_value*); -SQLITE_API sqlite3_int64 sqlite3_value_int64(sqlite3_value*); -SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value*); -SQLITE_API const void *sqlite3_value_text16(sqlite3_value*); -SQLITE_API const void *sqlite3_value_text16le(sqlite3_value*); -SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*); -SQLITE_API int sqlite3_value_type(sqlite3_value*); -SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); +SQLITE_API const void *SQLITE_STDCALL sqlite3_value_blob(sqlite3_value*); +SQLITE_API int SQLITE_STDCALL sqlite3_value_bytes(sqlite3_value*); +SQLITE_API int SQLITE_STDCALL sqlite3_value_bytes16(sqlite3_value*); +SQLITE_API double SQLITE_STDCALL sqlite3_value_double(sqlite3_value*); +SQLITE_API int SQLITE_STDCALL sqlite3_value_int(sqlite3_value*); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_value_int64(sqlite3_value*); +SQLITE_API const unsigned char *SQLITE_STDCALL sqlite3_value_text(sqlite3_value*); +SQLITE_API const void *SQLITE_STDCALL sqlite3_value_text16(sqlite3_value*); +SQLITE_API const void *SQLITE_STDCALL sqlite3_value_text16le(sqlite3_value*); +SQLITE_API const void *SQLITE_STDCALL sqlite3_value_text16be(sqlite3_value*); +SQLITE_API int SQLITE_STDCALL sqlite3_value_type(sqlite3_value*); +SQLITE_API int SQLITE_STDCALL sqlite3_value_numeric_type(sqlite3_value*); /* ** CAPI3REF: Obtain Aggregate Function Context +** METHOD: sqlite3_context ** ** Implementations of aggregate SQL functions use this ** routine to allocate memory for storing their state. @@ -4493,10 +4599,11 @@ SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); ** This routine must be called from the same thread in which ** the aggregate SQL function is running. */ -SQLITE_API void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); +SQLITE_API void *SQLITE_STDCALL sqlite3_aggregate_context(sqlite3_context*, int nBytes); /* ** CAPI3REF: User Data For Functions +** METHOD: sqlite3_context ** ** ^The sqlite3_user_data() interface returns a copy of ** the pointer that was the pUserData parameter (the 5th parameter) @@ -4507,10 +4614,11 @@ SQLITE_API void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); ** This routine must be called from the same thread in which ** the application-defined function is running. */ -SQLITE_API void *sqlite3_user_data(sqlite3_context*); +SQLITE_API void *SQLITE_STDCALL sqlite3_user_data(sqlite3_context*); /* ** CAPI3REF: Database Connection For Functions +** METHOD: sqlite3_context ** ** ^The sqlite3_context_db_handle() interface returns a copy of ** the pointer to the [database connection] (the 1st parameter) @@ -4518,10 +4626,11 @@ SQLITE_API void *sqlite3_user_data(sqlite3_context*); ** and [sqlite3_create_function16()] routines that originally ** registered the application defined function. */ -SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); +SQLITE_API sqlite3 *SQLITE_STDCALL sqlite3_context_db_handle(sqlite3_context*); /* ** CAPI3REF: Function Auxiliary Data +** METHOD: sqlite3_context ** ** These functions may be used by (non-aggregate) SQL functions to ** associate metadata with argument values. If the same value is passed to @@ -4570,8 +4679,8 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); ** These routines must be called from the same thread in which ** the SQL function is running. */ -SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); -SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); +SQLITE_API void *SQLITE_STDCALL sqlite3_get_auxdata(sqlite3_context*, int N); +SQLITE_API void SQLITE_STDCALL sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); /* @@ -4594,6 +4703,7 @@ typedef void (*sqlite3_destructor_type)(void*); /* ** CAPI3REF: Setting The Result Of An SQL Function +** METHOD: sqlite3_context ** ** These routines are used by the xFunc or xFinal callbacks that ** implement SQL functions and aggregates. See @@ -4706,29 +4816,30 @@ typedef void (*sqlite3_destructor_type)(void*); ** than the one containing the application-defined function that received ** 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*, +SQLITE_API void SQLITE_STDCALL sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*)); +SQLITE_API void SQLITE_STDCALL 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); -SQLITE_API void sqlite3_result_error_toobig(sqlite3_context*); -SQLITE_API void sqlite3_result_error_nomem(sqlite3_context*); -SQLITE_API void sqlite3_result_error_code(sqlite3_context*, int); -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, +SQLITE_API void SQLITE_STDCALL sqlite3_result_double(sqlite3_context*, double); +SQLITE_API void SQLITE_STDCALL sqlite3_result_error(sqlite3_context*, const char*, int); +SQLITE_API void SQLITE_STDCALL sqlite3_result_error16(sqlite3_context*, const void*, int); +SQLITE_API void SQLITE_STDCALL sqlite3_result_error_toobig(sqlite3_context*); +SQLITE_API void SQLITE_STDCALL sqlite3_result_error_nomem(sqlite3_context*); +SQLITE_API void SQLITE_STDCALL sqlite3_result_error_code(sqlite3_context*, int); +SQLITE_API void SQLITE_STDCALL sqlite3_result_int(sqlite3_context*, int); +SQLITE_API void SQLITE_STDCALL sqlite3_result_int64(sqlite3_context*, sqlite3_int64); +SQLITE_API void SQLITE_STDCALL sqlite3_result_null(sqlite3_context*); +SQLITE_API void SQLITE_STDCALL sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); +SQLITE_API void SQLITE_STDCALL 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*)); -SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); -SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); +SQLITE_API void SQLITE_STDCALL sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); +SQLITE_API void SQLITE_STDCALL sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); +SQLITE_API void SQLITE_STDCALL sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); +SQLITE_API void SQLITE_STDCALL sqlite3_result_value(sqlite3_context*, sqlite3_value*); +SQLITE_API void SQLITE_STDCALL sqlite3_result_zeroblob(sqlite3_context*, int n); /* ** CAPI3REF: Define New Collating Sequences +** METHOD: sqlite3 ** ** ^These functions add, remove, or modify a [collation] associated ** with the [database connection] specified as the first argument. @@ -4806,14 +4917,14 @@ SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); ** ** See also: [sqlite3_collation_needed()] and [sqlite3_collation_needed16()]. */ -SQLITE_API int sqlite3_create_collation( +SQLITE_API int SQLITE_STDCALL sqlite3_create_collation( sqlite3*, const char *zName, int eTextRep, void *pArg, int(*xCompare)(void*,int,const void*,int,const void*) ); -SQLITE_API int sqlite3_create_collation_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_create_collation_v2( sqlite3*, const char *zName, int eTextRep, @@ -4821,7 +4932,7 @@ SQLITE_API int sqlite3_create_collation_v2( int(*xCompare)(void*,int,const void*,int,const void*), void(*xDestroy)(void*) ); -SQLITE_API int sqlite3_create_collation16( +SQLITE_API int SQLITE_STDCALL sqlite3_create_collation16( sqlite3*, const void *zName, int eTextRep, @@ -4831,6 +4942,7 @@ SQLITE_API int sqlite3_create_collation16( /* ** CAPI3REF: Collation Needed Callbacks +** METHOD: sqlite3 ** ** ^To avoid having to register all collation sequences before a database ** can be used, a single callback function may be registered with the @@ -4855,12 +4967,12 @@ SQLITE_API int sqlite3_create_collation16( ** [sqlite3_create_collation()], [sqlite3_create_collation16()], or ** [sqlite3_create_collation_v2()]. */ -SQLITE_API int sqlite3_collation_needed( +SQLITE_API int SQLITE_STDCALL sqlite3_collation_needed( sqlite3*, void*, void(*)(void*,sqlite3*,int eTextRep,const char*) ); -SQLITE_API int sqlite3_collation_needed16( +SQLITE_API int SQLITE_STDCALL sqlite3_collation_needed16( sqlite3*, void*, void(*)(void*,sqlite3*,int eTextRep,const void*) @@ -4874,11 +4986,11 @@ SQLITE_API int sqlite3_collation_needed16( ** The code to implement this API is not available in the public release ** of SQLite. */ -SQLITE_API int sqlite3_key( +SQLITE_API int SQLITE_STDCALL sqlite3_key( sqlite3 *db, /* Database to be rekeyed */ const void *pKey, int nKey /* The key */ ); -SQLITE_API int sqlite3_key_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_key_v2( sqlite3 *db, /* Database to be rekeyed */ const char *zDbName, /* Name of the database */ const void *pKey, int nKey /* The key */ @@ -4892,11 +5004,11 @@ SQLITE_API int sqlite3_key_v2( ** The code to implement this API is not available in the public release ** of SQLite. */ -SQLITE_API int sqlite3_rekey( +SQLITE_API int SQLITE_STDCALL sqlite3_rekey( sqlite3 *db, /* Database to be rekeyed */ const void *pKey, int nKey /* The new key */ ); -SQLITE_API int sqlite3_rekey_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_rekey_v2( sqlite3 *db, /* Database to be rekeyed */ const char *zDbName, /* Name of the database */ const void *pKey, int nKey /* The new key */ @@ -4906,7 +5018,7 @@ SQLITE_API int sqlite3_rekey_v2( ** Specify the activation key for a SEE database. Unless ** activated, none of the SEE routines will work. */ -SQLITE_API void sqlite3_activate_see( +SQLITE_API void SQLITE_STDCALL sqlite3_activate_see( const char *zPassPhrase /* Activation phrase */ ); #endif @@ -4916,7 +5028,7 @@ SQLITE_API void sqlite3_activate_see( ** Specify the activation key for a CEROD database. Unless ** activated, none of the CEROD routines will work. */ -SQLITE_API void sqlite3_activate_cerod( +SQLITE_API void SQLITE_STDCALL sqlite3_activate_cerod( const char *zPassPhrase /* Activation phrase */ ); #endif @@ -4938,7 +5050,7 @@ SQLITE_API void sqlite3_activate_cerod( ** all, then the behavior of sqlite3_sleep() may deviate from the description ** in the previous paragraphs. */ -SQLITE_API int sqlite3_sleep(int); +SQLITE_API int SQLITE_STDCALL sqlite3_sleep(int); /* ** CAPI3REF: Name Of The Folder Holding Temporary Files @@ -5038,6 +5150,7 @@ SQLITE_API char *sqlite3_data_directory; /* ** CAPI3REF: Test For Auto-Commit Mode ** KEYWORDS: {autocommit mode} +** METHOD: sqlite3 ** ** ^The sqlite3_get_autocommit() interface returns non-zero or ** zero if the given database connection is or is not in autocommit mode, @@ -5056,10 +5169,11 @@ SQLITE_API char *sqlite3_data_directory; ** connection while this routine is running, then the return value ** is undefined. */ -SQLITE_API int sqlite3_get_autocommit(sqlite3*); +SQLITE_API int SQLITE_STDCALL sqlite3_get_autocommit(sqlite3*); /* ** CAPI3REF: Find The Database Handle Of A Prepared Statement +** METHOD: sqlite3_stmt ** ** ^The sqlite3_db_handle interface returns the [database connection] handle ** to which a [prepared statement] belongs. ^The [database connection] @@ -5068,10 +5182,11 @@ SQLITE_API int sqlite3_get_autocommit(sqlite3*); ** to the [sqlite3_prepare_v2()] call (or its variants) that was used to ** create the statement in the first place. */ -SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); +SQLITE_API sqlite3 *SQLITE_STDCALL sqlite3_db_handle(sqlite3_stmt*); /* ** CAPI3REF: Return The Filename For A Database Connection +** METHOD: sqlite3 ** ** ^The sqlite3_db_filename(D,N) interface returns a pointer to a filename ** associated with database N of connection D. ^The main database file @@ -5084,19 +5199,21 @@ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); ** will be an absolute pathname, even if the filename used ** to open the database originally was a URI or relative pathname. */ -SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName); +SQLITE_API const char *SQLITE_STDCALL sqlite3_db_filename(sqlite3 *db, const char *zDbName); /* ** CAPI3REF: Determine if a database is read-only +** METHOD: sqlite3 ** ** ^The sqlite3_db_readonly(D,N) interface returns 1 if the database N ** of connection D is read-only, 0 if it is read/write, or -1 if N is not ** the name of a database on connection D. */ -SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName); +SQLITE_API int SQLITE_STDCALL sqlite3_db_readonly(sqlite3 *db, const char *zDbName); /* ** CAPI3REF: Find the next prepared statement +** METHOD: sqlite3 ** ** ^This interface returns a pointer to the next [prepared statement] after ** pStmt associated with the [database connection] pDb. ^If pStmt is NULL @@ -5108,10 +5225,11 @@ SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName); ** [sqlite3_next_stmt(D,S)] must refer to an open database ** connection and in particular must not be a NULL pointer. */ -SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); +SQLITE_API sqlite3_stmt *SQLITE_STDCALL sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); /* ** CAPI3REF: Commit And Rollback Notification Callbacks +** METHOD: sqlite3 ** ** ^The sqlite3_commit_hook() interface registers a callback ** function to be invoked whenever a transaction is [COMMIT | committed]. @@ -5156,11 +5274,12 @@ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); ** ** See also the [sqlite3_update_hook()] interface. */ -SQLITE_API void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); -SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); +SQLITE_API void *SQLITE_STDCALL sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); +SQLITE_API void *SQLITE_STDCALL sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); /* ** CAPI3REF: Data Change Notification Callbacks +** METHOD: sqlite3 ** ** ^The sqlite3_update_hook() interface registers a callback function ** with the [database connection] identified by the first argument @@ -5207,7 +5326,7 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); ** See also the [sqlite3_commit_hook()] and [sqlite3_rollback_hook()] ** interfaces. */ -SQLITE_API void *sqlite3_update_hook( +SQLITE_API void *SQLITE_STDCALL sqlite3_update_hook( sqlite3*, void(*)(void *,int ,char const *,char const *,sqlite3_int64), void* @@ -5237,12 +5356,17 @@ SQLITE_API void *sqlite3_update_hook( ** future releases of SQLite. Applications that care about shared ** cache setting should set it explicitly. ** +** Note: This method is disabled on MacOS X 10.7 and iOS version 5.0 +** and will always return SQLITE_MISUSE. On those systems, +** shared cache mode should be enabled per-database connection via +** [sqlite3_open_v2()] with [SQLITE_OPEN_SHAREDCACHE]. +** ** This interface is threadsafe on processors where writing a ** 32-bit integer is atomic. ** ** See Also: [SQLite Shared-Cache Mode] */ -SQLITE_API int sqlite3_enable_shared_cache(int); +SQLITE_API int SQLITE_STDCALL sqlite3_enable_shared_cache(int); /* ** CAPI3REF: Attempt To Free Heap Memory @@ -5258,10 +5382,11 @@ SQLITE_API int sqlite3_enable_shared_cache(int); ** ** See also: [sqlite3_db_release_memory()] */ -SQLITE_API int sqlite3_release_memory(int); +SQLITE_API int SQLITE_STDCALL sqlite3_release_memory(int); /* ** CAPI3REF: Free Memory Used By A Database Connection +** METHOD: sqlite3 ** ** ^The sqlite3_db_release_memory(D) interface attempts to free as much heap ** memory as possible from database connection D. Unlike the @@ -5271,7 +5396,7 @@ SQLITE_API int sqlite3_release_memory(int); ** ** See also: [sqlite3_release_memory()] */ -SQLITE_API int sqlite3_db_release_memory(sqlite3*); +SQLITE_API int SQLITE_STDCALL sqlite3_db_release_memory(sqlite3*); /* ** CAPI3REF: Impose A Limit On Heap Size @@ -5323,7 +5448,7 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*); ** The circumstances under which SQLite will enforce the soft heap limit may ** changes in future releases of SQLite. */ -SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_soft_heap_limit64(sqlite3_int64 N); /* ** CAPI3REF: Deprecated Soft Heap Limit Interface @@ -5334,11 +5459,12 @@ SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N); ** only. All new applications should use the ** [sqlite3_soft_heap_limit64()] interface rather than this one. */ -SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N); +SQLITE_API SQLITE_DEPRECATED void SQLITE_STDCALL sqlite3_soft_heap_limit(int N); /* ** CAPI3REF: Extract Metadata About A Column Of A Table +** METHOD: sqlite3 ** ** ^(The sqlite3_table_column_metadata(X,D,T,C,....) routine returns ** information about column C of table T in database D @@ -5403,7 +5529,7 @@ SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N); ** parsed, if that has not already been done, and returns an error if ** any errors are encountered while loading the schema. */ -SQLITE_API int sqlite3_table_column_metadata( +SQLITE_API int SQLITE_STDCALL sqlite3_table_column_metadata( sqlite3 *db, /* Connection handle */ const char *zDbName, /* Database name or NULL */ const char *zTableName, /* Table name */ @@ -5417,6 +5543,7 @@ SQLITE_API int sqlite3_table_column_metadata( /* ** CAPI3REF: Load An Extension +** METHOD: sqlite3 ** ** ^This interface loads an SQLite extension library from the named file. ** @@ -5449,7 +5576,7 @@ SQLITE_API int sqlite3_table_column_metadata( ** ** See also the [load_extension() SQL function]. */ -SQLITE_API int sqlite3_load_extension( +SQLITE_API int SQLITE_STDCALL sqlite3_load_extension( sqlite3 *db, /* Load the extension into this database connection */ const char *zFile, /* Name of the shared library containing extension */ const char *zProc, /* Entry point. Derived from zFile if 0 */ @@ -5458,6 +5585,7 @@ SQLITE_API int sqlite3_load_extension( /* ** CAPI3REF: Enable Or Disable Extension Loading +** METHOD: sqlite3 ** ** ^So as not to open security holes in older applications that are ** unprepared to deal with [extension loading], and as a means of disabling @@ -5469,7 +5597,7 @@ SQLITE_API int sqlite3_load_extension( ** to turn extension loading on and call it with onoff==0 to turn ** it back off again. */ -SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); +SQLITE_API int SQLITE_STDCALL sqlite3_enable_load_extension(sqlite3 *db, int onoff); /* ** CAPI3REF: Automatically Load Statically Linked Extensions @@ -5507,7 +5635,7 @@ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); ** See also: [sqlite3_reset_auto_extension()] ** and [sqlite3_cancel_auto_extension()] */ -SQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void)); +SQLITE_API int SQLITE_STDCALL sqlite3_auto_extension(void (*xEntryPoint)(void)); /* ** CAPI3REF: Cancel Automatic Extension Loading @@ -5519,7 +5647,7 @@ SQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void)); ** unregistered and it returns 0 if X was not on the list of initialization ** routines. */ -SQLITE_API int sqlite3_cancel_auto_extension(void (*xEntryPoint)(void)); +SQLITE_API int SQLITE_STDCALL sqlite3_cancel_auto_extension(void (*xEntryPoint)(void)); /* ** CAPI3REF: Reset Automatic Extension Loading @@ -5527,7 +5655,7 @@ SQLITE_API int sqlite3_cancel_auto_extension(void (*xEntryPoint)(void)); ** ^This interface disables all automatic extensions previously ** registered using [sqlite3_auto_extension()]. */ -SQLITE_API void sqlite3_reset_auto_extension(void); +SQLITE_API void SQLITE_STDCALL sqlite3_reset_auto_extension(void); /* ** The interface to the virtual-table mechanism is currently considered @@ -5707,6 +5835,7 @@ struct sqlite3_index_info { /* ** CAPI3REF: Register A Virtual Table Implementation +** METHOD: sqlite3 ** ** ^These routines are used to register a new [virtual table module] name. ** ^Module names must be registered before @@ -5730,13 +5859,13 @@ struct sqlite3_index_info { ** interface is equivalent to sqlite3_create_module_v2() with a NULL ** destructor. */ -SQLITE_API int sqlite3_create_module( +SQLITE_API int SQLITE_STDCALL sqlite3_create_module( sqlite3 *db, /* SQLite connection to register module with */ const char *zName, /* Name of the module */ const sqlite3_module *p, /* Methods for the module */ void *pClientData /* Client data for xCreate/xConnect */ ); -SQLITE_API int sqlite3_create_module_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_create_module_v2( sqlite3 *db, /* SQLite connection to register module with */ const char *zName, /* Name of the module */ const sqlite3_module *p, /* Methods for the module */ @@ -5764,7 +5893,7 @@ SQLITE_API int sqlite3_create_module_v2( */ struct sqlite3_vtab { const sqlite3_module *pModule; /* The module for this virtual table */ - int nRef; /* NO LONGER USED */ + int nRef; /* Number of open cursors */ char *zErrMsg; /* Error message from sqlite3_mprintf() */ /* Virtual table implementations will typically add additional fields */ }; @@ -5799,10 +5928,11 @@ struct sqlite3_vtab_cursor { ** to declare the format (the names and datatypes of the columns) of ** the virtual tables they implement. */ -SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL); +SQLITE_API int SQLITE_STDCALL sqlite3_declare_vtab(sqlite3*, const char *zSQL); /* ** CAPI3REF: Overload A Function For A Virtual Table +** METHOD: sqlite3 ** ** ^(Virtual tables can provide alternative implementations of functions ** using the [xFindFunction] method of the [virtual table module]. @@ -5817,7 +5947,7 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL); ** purpose is to be a placeholder function that can be overloaded ** by a [virtual table]. */ -SQLITE_API int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg); +SQLITE_API int SQLITE_STDCALL sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg); /* ** The interface to the virtual-table mechanism defined above (back up @@ -5845,6 +5975,8 @@ typedef struct sqlite3_blob sqlite3_blob; /* ** CAPI3REF: Open A BLOB For Incremental I/O +** METHOD: sqlite3 +** CONSTRUCTOR: sqlite3_blob ** ** ^(This interfaces opens a [BLOB handle | handle] to the BLOB located ** in row iRow, column zColumn, table zTable in database zDb; @@ -5914,7 +6046,7 @@ typedef struct sqlite3_blob sqlite3_blob; ** To avoid a resource leak, every open [BLOB handle] should eventually ** be released by a call to [sqlite3_blob_close()]. */ -SQLITE_API int sqlite3_blob_open( +SQLITE_API int SQLITE_STDCALL sqlite3_blob_open( sqlite3*, const char *zDb, const char *zTable, @@ -5926,6 +6058,7 @@ SQLITE_API int sqlite3_blob_open( /* ** CAPI3REF: Move a BLOB Handle to a New Row +** METHOD: sqlite3_blob ** ** ^This function is used to move an existing blob handle so that it points ** to a different row of the same database table. ^The new row is identified @@ -5946,10 +6079,11 @@ SQLITE_API int sqlite3_blob_open( ** ** ^This function sets the database handle error code and message. */ -SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64); +SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64); /* ** CAPI3REF: Close A BLOB Handle +** DESTRUCTOR: sqlite3_blob ** ** ^This function closes an open [BLOB handle]. ^(The BLOB handle is closed ** unconditionally. Even if this routine returns an error code, the @@ -5968,10 +6102,11 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_i ** is passed a valid open blob handle, the values returned by the ** sqlite3_errcode() and sqlite3_errmsg() functions are set before returning. */ -SQLITE_API int sqlite3_blob_close(sqlite3_blob *); +SQLITE_API int SQLITE_STDCALL sqlite3_blob_close(sqlite3_blob *); /* ** CAPI3REF: Return The Size Of An Open BLOB +** METHOD: sqlite3_blob ** ** ^Returns the size in bytes of the BLOB accessible via the ** successfully opened [BLOB handle] in its only argument. ^The @@ -5983,10 +6118,11 @@ SQLITE_API int sqlite3_blob_close(sqlite3_blob *); ** been closed by [sqlite3_blob_close()]. Passing any other pointer in ** to this routine results in undefined and probably undesirable behavior. */ -SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *); +SQLITE_API int SQLITE_STDCALL sqlite3_blob_bytes(sqlite3_blob *); /* ** CAPI3REF: Read Data From A BLOB Incrementally +** METHOD: sqlite3_blob ** ** ^(This function is used to read data from an open [BLOB handle] into a ** caller-supplied buffer. N bytes of data are copied into buffer Z @@ -6011,10 +6147,11 @@ SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *); ** ** See also: [sqlite3_blob_write()]. */ -SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); +SQLITE_API int SQLITE_STDCALL sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); /* ** CAPI3REF: Write Data Into A BLOB Incrementally +** METHOD: sqlite3_blob ** ** ^(This function is used to write data into an open [BLOB handle] from a ** caller-supplied buffer. N bytes of data are copied from the buffer Z @@ -6052,7 +6189,7 @@ SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); ** ** See also: [sqlite3_blob_read()]. */ -SQLITE_API int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); +SQLITE_API int SQLITE_STDCALL sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); /* ** CAPI3REF: Virtual File System Objects @@ -6083,9 +6220,9 @@ SQLITE_API int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOff ** ^(If the default VFS is unregistered, another VFS is chosen as ** the default. The choice for the new VFS is arbitrary.)^ */ -SQLITE_API sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName); -SQLITE_API int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt); -SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); +SQLITE_API sqlite3_vfs *SQLITE_STDCALL sqlite3_vfs_find(const char *zVfsName); +SQLITE_API int SQLITE_STDCALL sqlite3_vfs_register(sqlite3_vfs*, int makeDflt); +SQLITE_API int SQLITE_STDCALL sqlite3_vfs_unregister(sqlite3_vfs*); /* ** CAPI3REF: Mutexes @@ -6198,11 +6335,11 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); ** ** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. */ -SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int); -SQLITE_API void sqlite3_mutex_free(sqlite3_mutex*); -SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex*); -SQLITE_API int sqlite3_mutex_try(sqlite3_mutex*); -SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex*); +SQLITE_API sqlite3_mutex *SQLITE_STDCALL sqlite3_mutex_alloc(int); +SQLITE_API void SQLITE_STDCALL sqlite3_mutex_free(sqlite3_mutex*); +SQLITE_API void SQLITE_STDCALL sqlite3_mutex_enter(sqlite3_mutex*); +SQLITE_API int SQLITE_STDCALL sqlite3_mutex_try(sqlite3_mutex*); +SQLITE_API void SQLITE_STDCALL sqlite3_mutex_leave(sqlite3_mutex*); /* ** CAPI3REF: Mutex Methods Object @@ -6312,8 +6449,8 @@ struct sqlite3_mutex_methods { ** interface should also return 1 when given a NULL pointer. */ #ifndef NDEBUG -SQLITE_API int sqlite3_mutex_held(sqlite3_mutex*); -SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); +SQLITE_API int SQLITE_STDCALL sqlite3_mutex_held(sqlite3_mutex*); +SQLITE_API int SQLITE_STDCALL sqlite3_mutex_notheld(sqlite3_mutex*); #endif /* @@ -6342,6 +6479,7 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); /* ** CAPI3REF: Retrieve the mutex for a database connection +** METHOD: sqlite3 ** ** ^This interface returns a pointer the [sqlite3_mutex] object that ** serializes access to the [database connection] given in the argument @@ -6349,10 +6487,11 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); ** ^If the [threading mode] is Single-thread or Multi-thread then this ** routine returns a NULL pointer. */ -SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*); +SQLITE_API sqlite3_mutex *SQLITE_STDCALL sqlite3_db_mutex(sqlite3*); /* ** CAPI3REF: Low-Level Control Of Database Files +** METHOD: sqlite3 ** ** ^The [sqlite3_file_control()] interface makes a direct call to the ** xFileControl method for the [sqlite3_io_methods] object associated @@ -6383,7 +6522,7 @@ SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*); ** ** See also: [SQLITE_FCNTL_LOCKSTATE] */ -SQLITE_API int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*); +SQLITE_API int SQLITE_STDCALL sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*); /* ** CAPI3REF: Testing Interface @@ -6402,7 +6541,7 @@ SQLITE_API int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void* ** Unlike most of the SQLite API, this function is not guaranteed to ** operate consistently from one release to the next. */ -SQLITE_API int sqlite3_test_control(int op, ...); +SQLITE_API int SQLITE_CDECL sqlite3_test_control(int op, ...); /* ** CAPI3REF: Testing Interface Operation Codes @@ -6436,12 +6575,13 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_BYTEORDER 22 #define SQLITE_TESTCTRL_ISINIT 23 #define SQLITE_TESTCTRL_SORTER_MMAP 24 -#define SQLITE_TESTCTRL_LAST 24 +#define SQLITE_TESTCTRL_IMPOSTER 25 +#define SQLITE_TESTCTRL_LAST 25 /* ** CAPI3REF: SQLite Runtime Status ** -** ^This interface is used to retrieve runtime status information +** ^These interfaces are used to retrieve runtime status information ** about the performance of SQLite, and optionally to reset various ** highwater marks. ^The first argument is an integer code for ** the specific parameter to measure. ^(Recognized integer codes @@ -6455,19 +6595,22 @@ SQLITE_API int sqlite3_test_control(int op, ...); ** ^(Other parameters record only the highwater mark and not the current ** value. For these latter parameters nothing is written into *pCurrent.)^ ** -** ^The sqlite3_status() routine returns SQLITE_OK on success and a -** non-zero [error code] on failure. +** ^The sqlite3_status() and sqlite3_status64() routines return +** SQLITE_OK on success and a non-zero [error code] on failure. ** -** This routine is threadsafe but is not atomic. This routine can be -** called while other threads are running the same or different SQLite -** interfaces. However the values returned in *pCurrent and -** *pHighwater reflect the status of SQLite at different points in time -** and it is possible that another thread might change the parameter -** in between the times when *pCurrent and *pHighwater are written. +** If either the current value or the highwater mark is too large to +** be represented by a 32-bit integer, then the values returned by +** sqlite3_status() are undefined. ** ** See also: [sqlite3_db_status()] */ -SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag); +SQLITE_API int SQLITE_STDCALL sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag); +SQLITE_API int SQLITE_STDCALL sqlite3_status64( + int op, + sqlite3_int64 *pCurrent, + sqlite3_int64 *pHighwater, + int resetFlag +); /* @@ -6565,6 +6708,7 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF /* ** CAPI3REF: Database Connection Status +** METHOD: sqlite3 ** ** ^This interface is used to retrieve runtime status information ** about a single [database connection]. ^The first argument is the @@ -6585,7 +6729,7 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF ** ** See also: [sqlite3_status()] and [sqlite3_stmt_status()]. */ -SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); +SQLITE_API int SQLITE_STDCALL sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); /* ** CAPI3REF: Status Parameters for database connections @@ -6693,6 +6837,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r /* ** CAPI3REF: Prepared Statement Status +** METHOD: sqlite3_stmt ** ** ^(Each prepared statement maintains various ** [SQLITE_STMTSTATUS counters] that measure the number @@ -6714,7 +6859,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r ** ** See also: [sqlite3_status()] and [sqlite3_db_status()]. */ -SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); +SQLITE_API int SQLITE_STDCALL sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); /* ** CAPI3REF: Status Parameters for prepared statements @@ -7137,20 +7282,20 @@ typedef struct sqlite3_backup sqlite3_backup; ** is not a permanent error and does not affect the return value of ** sqlite3_backup_finish(). ** -** [[sqlite3_backup__remaining()]] [[sqlite3_backup_pagecount()]] +** [[sqlite3_backup_remaining()]] [[sqlite3_backup_pagecount()]] ** sqlite3_backup_remaining() and sqlite3_backup_pagecount() ** -** ^Each call to sqlite3_backup_step() sets two values inside -** the [sqlite3_backup] object: the number of pages still to be backed -** up and the total number of pages in the source database file. -** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces -** retrieve these two values, respectively. -** -** ^The values returned by these functions are only updated by -** sqlite3_backup_step(). ^If the source database is modified during a backup -** operation, then the values are not updated to account for any extra -** pages that need to be updated or the size of the source database file -** changing. +** ^The sqlite3_backup_remaining() routine returns the number of pages still +** to be backed up at the conclusion of the most recent sqlite3_backup_step(). +** ^The sqlite3_backup_pagecount() routine returns the total number of pages +** in the source database at the conclusion of the most recent +** sqlite3_backup_step(). +** ^(The values returned by these functions are only updated by +** sqlite3_backup_step(). If the source database is modified in a way that +** changes the size of the source database or the number of pages remaining, +** those changes are not reflected in the output of sqlite3_backup_pagecount() +** and sqlite3_backup_remaining() until after the next +** sqlite3_backup_step().)^ ** ** Concurrent Usage of Database Handles ** @@ -7183,19 +7328,20 @@ typedef struct sqlite3_backup sqlite3_backup; ** same time as another thread is invoking sqlite3_backup_step() it is ** possible that they return invalid values. */ -SQLITE_API sqlite3_backup *sqlite3_backup_init( +SQLITE_API sqlite3_backup *SQLITE_STDCALL sqlite3_backup_init( sqlite3 *pDest, /* Destination database handle */ const char *zDestName, /* Destination database name */ sqlite3 *pSource, /* Source database handle */ const char *zSourceName /* Source database name */ ); -SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage); -SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p); -SQLITE_API int sqlite3_backup_remaining(sqlite3_backup *p); -SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); +SQLITE_API int SQLITE_STDCALL sqlite3_backup_step(sqlite3_backup *p, int nPage); +SQLITE_API int SQLITE_STDCALL sqlite3_backup_finish(sqlite3_backup *p); +SQLITE_API int SQLITE_STDCALL sqlite3_backup_remaining(sqlite3_backup *p); +SQLITE_API int SQLITE_STDCALL sqlite3_backup_pagecount(sqlite3_backup *p); /* ** CAPI3REF: Unlock Notification +** METHOD: sqlite3 ** ** ^When running in shared-cache mode, a database operation may fail with ** an [SQLITE_LOCKED] error if the required locks on the shared-cache or @@ -7308,7 +7454,7 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); ** the special "DROP TABLE/INDEX" case, the extended error code is just ** SQLITE_LOCKED.)^ */ -SQLITE_API int sqlite3_unlock_notify( +SQLITE_API int SQLITE_STDCALL sqlite3_unlock_notify( sqlite3 *pBlocked, /* Waiting connection */ void (*xNotify)(void **apArg, int nArg), /* Callback function to invoke */ void *pNotifyArg /* Argument to pass to xNotify */ @@ -7323,8 +7469,8 @@ SQLITE_API int sqlite3_unlock_notify( ** strings in a case-independent fashion, using the same definition of "case ** independence" that SQLite uses internally when comparing identifiers. */ -SQLITE_API int sqlite3_stricmp(const char *, const char *); -SQLITE_API int sqlite3_strnicmp(const char *, const char *, int); +SQLITE_API int SQLITE_STDCALL sqlite3_stricmp(const char *, const char *); +SQLITE_API int SQLITE_STDCALL sqlite3_strnicmp(const char *, const char *, int); /* ** CAPI3REF: String Globbing @@ -7339,7 +7485,7 @@ SQLITE_API int sqlite3_strnicmp(const char *, const char *, int); ** Note that this routine returns zero on a match and non-zero if the strings ** do not match, the same as [sqlite3_stricmp()] and [sqlite3_strnicmp()]. */ -SQLITE_API int sqlite3_strglob(const char *zGlob, const char *zStr); +SQLITE_API int SQLITE_STDCALL sqlite3_strglob(const char *zGlob, const char *zStr); /* ** CAPI3REF: Error Logging Interface @@ -7362,10 +7508,11 @@ SQLITE_API int sqlite3_strglob(const char *zGlob, const char *zStr); ** a few hundred characters, it will be truncated to the length of the ** buffer. */ -SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...); +SQLITE_API void SQLITE_CDECL sqlite3_log(int iErrCode, const char *zFormat, ...); /* ** CAPI3REF: Write-Ahead Log Commit Hook +** METHOD: sqlite3 ** ** ^The [sqlite3_wal_hook()] function is used to register a callback that ** is invoked each time data is committed to a database in wal mode. @@ -7397,7 +7544,7 @@ SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...); ** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will ** those overwrite any prior [sqlite3_wal_hook()] settings. */ -SQLITE_API void *sqlite3_wal_hook( +SQLITE_API void *SQLITE_STDCALL sqlite3_wal_hook( sqlite3*, int(*)(void *,sqlite3*,const char*,int), void* @@ -7405,6 +7552,7 @@ SQLITE_API void *sqlite3_wal_hook( /* ** CAPI3REF: Configure an auto-checkpoint +** METHOD: sqlite3 ** ** ^The [sqlite3_wal_autocheckpoint(D,N)] is a wrapper around ** [sqlite3_wal_hook()] that causes any database on [database connection] D @@ -7431,10 +7579,11 @@ SQLITE_API void *sqlite3_wal_hook( ** is only necessary if the default setting is found to be suboptimal ** for a particular application. */ -SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N); +SQLITE_API int SQLITE_STDCALL sqlite3_wal_autocheckpoint(sqlite3 *db, int N); /* ** CAPI3REF: Checkpoint a database +** METHOD: sqlite3 ** ** ^(The sqlite3_wal_checkpoint(D,X) is equivalent to ** [sqlite3_wal_checkpoint_v2](D,X,[SQLITE_CHECKPOINT_PASSIVE],0,0).)^ @@ -7452,10 +7601,11 @@ SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N); ** start a callback but which do not need the full power (and corresponding ** complication) of [sqlite3_wal_checkpoint_v2()]. */ -SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); +SQLITE_API int SQLITE_STDCALL sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); /* ** CAPI3REF: Checkpoint a database +** METHOD: sqlite3 ** ** ^(The sqlite3_wal_checkpoint_v2(D,X,M,L,C) interface runs a checkpoint ** operation on database X of [database connection] D in mode M. Status @@ -7545,7 +7695,7 @@ SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); ** ^The [PRAGMA wal_checkpoint] command can be used to invoke this interface ** from SQL. */ -SQLITE_API int sqlite3_wal_checkpoint_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_wal_checkpoint_v2( sqlite3 *db, /* Database handle */ const char *zDb, /* Name of attached database (or NULL) */ int eMode, /* SQLITE_CHECKPOINT_* value */ @@ -7581,7 +7731,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2( ** this function. (See [SQLITE_VTAB_CONSTRAINT_SUPPORT].) Further options ** may be added in the future. */ -SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); +SQLITE_API int SQLITE_CDECL sqlite3_vtab_config(sqlite3*, int op, ...); /* ** CAPI3REF: Virtual Table Configuration Options @@ -7634,7 +7784,7 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); ** of the SQL statement that triggered the call to the [xUpdate] method of the ** [virtual table]. */ -SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); +SQLITE_API int SQLITE_STDCALL sqlite3_vtab_on_conflict(sqlite3 *); /* ** CAPI3REF: Conflict resolution modes @@ -7710,6 +7860,7 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); /* ** CAPI3REF: Prepared Statement Scan Status +** METHOD: sqlite3_stmt ** ** This interface returns information about the predicted and measured ** performance for pStmt. Advanced applications can use this @@ -7738,7 +7889,7 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); ** ** See also: [sqlite3_stmt_scanstatus_reset()] */ -SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_stmt_scanstatus( +SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_stmt_scanstatus( sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ int idx, /* Index of loop to report on */ int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ @@ -7747,13 +7898,14 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_stmt_scanstatus( /* ** CAPI3REF: Zero Scan-Status Counters +** METHOD: sqlite3_stmt ** ** ^Zero all [sqlite3_stmt_scanstatus()] related event counters. ** ** This API is only available if the library is built with pre-processor ** symbol [SQLITE_ENABLE_STMT_SCANSTATUS] defined. */ -SQLITE_API SQLITE_EXPERIMENTAL void sqlite3_stmt_scanstatus_reset(sqlite3_stmt*); +SQLITE_API SQLITE_EXPERIMENTAL void SQLITE_STDCALL sqlite3_stmt_scanstatus_reset(sqlite3_stmt*); /* @@ -7808,7 +7960,7 @@ typedef struct sqlite3_rtree_query_info sqlite3_rtree_query_info; ** ** SELECT ... FROM WHERE MATCH $zGeom(... params ...) */ -SQLITE_API int sqlite3_rtree_geometry_callback( +SQLITE_API int SQLITE_STDCALL sqlite3_rtree_geometry_callback( sqlite3 *db, const char *zGeom, int (*xGeom)(sqlite3_rtree_geometry*, int, sqlite3_rtree_dbl*,int*), @@ -7834,7 +7986,7 @@ struct sqlite3_rtree_geometry { ** ** SELECT ... FROM WHERE MATCH $zQueryFunc(... params ...) */ -SQLITE_API int sqlite3_rtree_query_callback( +SQLITE_API int SQLITE_STDCALL sqlite3_rtree_query_callback( sqlite3 *db, const char *zQueryFunc, int (*xQueryFunc)(sqlite3_rtree_query_info*), @@ -7998,15 +8150,17 @@ struct sqlite3_rtree_query_info { #endif /* -** The maximum number of in-memory pages to use for the main database -** table and for temporary tables. The SQLITE_DEFAULT_CACHE_SIZE +** The suggested maximum number of in-memory pages to use for +** the main database table and for temporary tables. +** +** IMPLEMENTATION-OF: R-31093-59126 The default suggested cache size +** is 2000 pages. +** IMPLEMENTATION-OF: R-48205-43578 The default suggested cache size can be +** altered using the SQLITE_DEFAULT_CACHE_SIZE compile-time options. */ #ifndef SQLITE_DEFAULT_CACHE_SIZE # define SQLITE_DEFAULT_CACHE_SIZE 2000 #endif -#ifndef SQLITE_DEFAULT_TEMP_CACHE_SIZE -# define SQLITE_DEFAULT_TEMP_CACHE_SIZE 500 -#endif /* ** The default number of frames to accumulate in the log file before @@ -8355,6 +8509,32 @@ SQLITE_PRIVATE void sqlite3Coverage(int); # define NEVER(X) (X) #endif +/* +** Declarations used for tracing the operating system interfaces. +*/ +#if defined(SQLITE_FORCE_OS_TRACE) || defined(SQLITE_TEST) || \ + (defined(SQLITE_DEBUG) && SQLITE_OS_WIN) + extern int sqlite3OSTrace; +# define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X +# define SQLITE_HAVE_OS_TRACE +#else +# define OSTRACE(X) +# undef SQLITE_HAVE_OS_TRACE +#endif + +/* +** Is the sqlite3ErrName() function needed in the build? Currently, +** it is needed by "mutex_w32.c" (when debugging), "os_win.c" (when +** OSTRACE is enabled), and by several "test*.c" files (which are +** compiled using SQLITE_TEST). +*/ +#if defined(SQLITE_HAVE_OS_TRACE) || defined(SQLITE_TEST) || \ + (defined(SQLITE_DEBUG) && SQLITE_OS_WIN) +# define SQLITE_NEED_ERR_NAME +#else +# undef SQLITE_NEED_ERR_NAME +#endif + /* ** Return true (non-zero) if the input is an integer that is too large ** to fit in 32-bits. This macro is used inside of various testcase() @@ -8850,6 +9030,20 @@ typedef INT8_TYPE i8; /* 1-byte signed integer */ */ typedef INT16_TYPE LogEst; +/* +** Set the SQLITE_PTRSIZE macro to the number of bytes in a pointer +*/ +#ifndef SQLITE_PTRSIZE +# if defined(__SIZEOF_POINTER__) +# define SQLITE_PTRSIZE __SIZEOF_POINTER__ +# elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \ + defined(_M_ARM) || defined(__arm__) || defined(__x86) +# define SQLITE_PTRSIZE 4 +# else +# define SQLITE_PTRSIZE 8 +# endif +#endif + /* ** Macros to determine whether the machine is big or little endian, ** and whether or not that determination is run-time or compile-time. @@ -9062,8 +9256,8 @@ struct BusyHandler { #define SQLITE_WSD const #define GLOBAL(t,v) (*(t*)sqlite3_wsd_find((void*)&(v), sizeof(v))) #define sqlite3GlobalConfig GLOBAL(struct Sqlite3Config, sqlite3Config) -SQLITE_API int sqlite3_wsd_init(int N, int J); -SQLITE_API void *sqlite3_wsd_find(void *K, int L); +SQLITE_API int SQLITE_STDCALL sqlite3_wsd_init(int N, int J); +SQLITE_API void *SQLITE_STDCALL sqlite3_wsd_find(void *K, int L); #else #define SQLITE_WSD #define GLOBAL(t,v) v @@ -9221,10 +9415,8 @@ SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*); SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree*,int); SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree*); SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree*,int); -SQLITE_PRIVATE int sqlite3BtreeGetReserve(Btree*); -#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_DEBUG) +SQLITE_PRIVATE int sqlite3BtreeGetOptimalReserve(Btree*); SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p); -#endif SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int); SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *); SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int); @@ -9302,8 +9494,18 @@ SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p); /* ** Values that may be OR'd together to form the second argument of an ** sqlite3BtreeCursorHints() call. +** +** The BTREE_BULKLOAD flag is set on index cursors when the index is going +** to be filled with content that is already in sorted order. +** +** The BTREE_SEEK_EQ flag is set on cursors that will get OP_SeekGE or +** OP_SeekLE opcodes for a range search, but where the range of entries +** selected will all have the same key. In other words, the cursor will +** be used only for equality key searches. +** */ -#define BTREE_BULKLOAD 0x00000001 +#define BTREE_BULKLOAD 0x00000001 /* Used to full index in sorted order */ +#define BTREE_SEEK_EQ 0x00000002 /* EQ seeks only - no range seeks */ SQLITE_PRIVATE int sqlite3BtreeCursor( Btree*, /* BTree containing table to open */ @@ -9349,6 +9551,9 @@ SQLITE_PRIVATE void sqlite3BtreeIncrblobCursor(BtCursor *); SQLITE_PRIVATE void sqlite3BtreeClearCursor(BtCursor *); SQLITE_PRIVATE int sqlite3BtreeSetVersion(Btree *pBt, int iVersion); SQLITE_PRIVATE void sqlite3BtreeCursorHints(BtCursor *, unsigned int mask); +#ifdef SQLITE_DEBUG +SQLITE_PRIVATE int sqlite3BtreeCursorHasHint(BtCursor*, unsigned int mask); +#endif SQLITE_PRIVATE int sqlite3BtreeIsReadonly(Btree *pBt); SQLITE_PRIVATE int sqlite3HeaderSizeBtree(void); @@ -9715,23 +9920,25 @@ typedef struct VdbeOpList VdbeOpList; #define OP_MemMax 136 /* synopsis: r[P1]=max(r[P1],r[P2]) */ #define OP_IfPos 137 /* synopsis: if r[P1]>0 goto P2 */ #define OP_IfNeg 138 /* synopsis: r[P1]+=P3, if r[P1]<0 goto P2 */ -#define OP_IfZero 139 /* synopsis: r[P1]+=P3, if r[P1]==0 goto P2 */ -#define OP_AggFinal 140 /* synopsis: accum=r[P1] N=P2 */ -#define OP_IncrVacuum 141 -#define OP_Expire 142 -#define OP_TableLock 143 /* synopsis: iDb=P1 root=P2 write=P3 */ -#define OP_VBegin 144 -#define OP_VCreate 145 -#define OP_VDestroy 146 -#define OP_VOpen 147 -#define OP_VColumn 148 /* synopsis: r[P3]=vcolumn(P2) */ -#define OP_VNext 149 -#define OP_VRename 150 -#define OP_Pagecount 151 -#define OP_MaxPgcnt 152 -#define OP_Init 153 /* synopsis: Start at P2 */ -#define OP_Noop 154 -#define OP_Explain 155 +#define OP_IfNotZero 139 /* synopsis: if r[P1]!=0 then r[P1]+=P3, goto P2 */ +#define OP_DecrJumpZero 140 /* synopsis: if (--r[P1])==0 goto P2 */ +#define OP_JumpZeroIncr 141 /* synopsis: if (r[P1]++)==0 ) goto P2 */ +#define OP_AggFinal 142 /* synopsis: accum=r[P1] N=P2 */ +#define OP_IncrVacuum 143 +#define OP_Expire 144 +#define OP_TableLock 145 /* synopsis: iDb=P1 root=P2 write=P3 */ +#define OP_VBegin 146 +#define OP_VCreate 147 +#define OP_VDestroy 148 +#define OP_VOpen 149 +#define OP_VColumn 150 /* synopsis: r[P3]=vcolumn(P2) */ +#define OP_VNext 151 +#define OP_VRename 152 +#define OP_Pagecount 153 +#define OP_MaxPgcnt 154 +#define OP_Init 155 /* synopsis: Start at P2 */ +#define OP_Noop 156 +#define OP_Explain 157 /* Properties such as "out2" or "jump" that are specified in @@ -9739,33 +9946,32 @@ typedef struct VdbeOpList VdbeOpList; ** are encoded into bitvectors as follows: */ #define OPFLG_JUMP 0x0001 /* jump: P2 holds jmp target */ -#define OPFLG_OUT2_PRERELEASE 0x0002 /* out2-prerelease: */ -#define OPFLG_IN1 0x0004 /* in1: P1 is an input */ -#define OPFLG_IN2 0x0008 /* in2: P2 is an input */ -#define OPFLG_IN3 0x0010 /* in3: P3 is an input */ -#define OPFLG_OUT2 0x0020 /* out2: P2 is an output */ -#define OPFLG_OUT3 0x0040 /* out3: P3 is an output */ +#define OPFLG_IN1 0x0002 /* in1: P1 is an input */ +#define OPFLG_IN2 0x0004 /* in2: P2 is an input */ +#define OPFLG_IN3 0x0008 /* in3: P3 is an input */ +#define OPFLG_OUT2 0x0010 /* out2: P2 is an output */ +#define OPFLG_OUT3 0x0020 /* out3: P3 is an output */ #define OPFLG_INITIALIZER {\ /* 0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,\ -/* 8 */ 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,\ -/* 16 */ 0x01, 0x01, 0x04, 0x24, 0x01, 0x04, 0x05, 0x10,\ -/* 24 */ 0x00, 0x02, 0x02, 0x02, 0x02, 0x00, 0x02, 0x02,\ -/* 32 */ 0x00, 0x00, 0x20, 0x00, 0x00, 0x04, 0x05, 0x04,\ -/* 40 */ 0x04, 0x00, 0x00, 0x01, 0x01, 0x05, 0x05, 0x00,\ -/* 48 */ 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x00, 0x00,\ -/* 56 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11,\ -/* 64 */ 0x11, 0x11, 0x08, 0x11, 0x11, 0x11, 0x11, 0x4c,\ -/* 72 */ 0x4c, 0x02, 0x02, 0x00, 0x05, 0x05, 0x15, 0x15,\ -/* 80 */ 0x15, 0x15, 0x15, 0x15, 0x00, 0x4c, 0x4c, 0x4c,\ -/* 88 */ 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x00,\ -/* 96 */ 0x24, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,\ -/* 104 */ 0x00, 0x01, 0x01, 0x01, 0x01, 0x08, 0x08, 0x00,\ -/* 112 */ 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x00,\ -/* 120 */ 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 128 */ 0x0c, 0x45, 0x15, 0x01, 0x02, 0x02, 0x00, 0x01,\ -/* 136 */ 0x08, 0x05, 0x05, 0x05, 0x00, 0x01, 0x00, 0x00,\ -/* 144 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02,\ -/* 152 */ 0x02, 0x01, 0x00, 0x00,} +/* 8 */ 0x01, 0x01, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,\ +/* 16 */ 0x01, 0x01, 0x02, 0x12, 0x01, 0x02, 0x03, 0x08,\ +/* 24 */ 0x00, 0x10, 0x10, 0x10, 0x10, 0x00, 0x10, 0x10,\ +/* 32 */ 0x00, 0x00, 0x10, 0x00, 0x00, 0x02, 0x03, 0x02,\ +/* 40 */ 0x02, 0x00, 0x00, 0x01, 0x01, 0x03, 0x03, 0x00,\ +/* 48 */ 0x00, 0x00, 0x10, 0x10, 0x08, 0x00, 0x00, 0x00,\ +/* 56 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x09,\ +/* 64 */ 0x09, 0x09, 0x04, 0x09, 0x09, 0x09, 0x09, 0x26,\ +/* 72 */ 0x26, 0x10, 0x10, 0x00, 0x03, 0x03, 0x0b, 0x0b,\ +/* 80 */ 0x0b, 0x0b, 0x0b, 0x0b, 0x00, 0x26, 0x26, 0x26,\ +/* 88 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x00,\ +/* 96 */ 0x12, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,\ +/* 104 */ 0x00, 0x01, 0x01, 0x01, 0x01, 0x04, 0x04, 0x00,\ +/* 112 */ 0x10, 0x01, 0x01, 0x01, 0x01, 0x10, 0x00, 0x00,\ +/* 120 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 128 */ 0x06, 0x23, 0x0b, 0x01, 0x10, 0x10, 0x00, 0x01,\ +/* 136 */ 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x01,\ +/* 144 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,\ +/* 152 */ 0x00, 0x10, 0x10, 0x01, 0x00, 0x00,} /************** End of opcodes.h *********************************************/ /************** Continuing where we left off in vdbe.h ***********************/ @@ -9824,6 +10030,7 @@ SQLITE_PRIVATE int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*); SQLITE_PRIVATE void sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,UnpackedRecord*); SQLITE_PRIVATE int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*); +SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(int, const void *, UnpackedRecord *, int); SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo *, char *, int, char **); typedef int (*RecordCompare)(int,const void*,UnpackedRecord*); @@ -10841,11 +11048,13 @@ struct sqlite3 { u8 iDb; /* Which db file is being initialized */ u8 busy; /* TRUE if currently initializing */ u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */ + u8 imposterTable; /* Building an imposter table */ } init; int nVdbeActive; /* Number of VDBEs currently running */ int nVdbeRead; /* Number of active VDBEs that read or write */ int nVdbeWrite; /* Number of active VDBEs that read and write */ int nVdbeExec; /* Number of nested calls to VdbeExec() */ + int nVDestroy; /* Number of active OP_VDestroy operations */ int nExtension; /* Number of loaded extensions */ void **aExtension; /* Array of shared library handles */ void (*xTrace)(void*,const char*); /* Trace function */ @@ -10959,6 +11168,7 @@ struct sqlite3 { #define SQLITE_DeferFKs 0x01000000 /* Defer all FK constraints */ #define SQLITE_QueryOnly 0x02000000 /* Disable database changes */ #define SQLITE_VdbeEQP 0x04000000 /* Debug EXPLAIN QUERY PLAN */ +#define SQLITE_Vacuum 0x08000000 /* Currently in a VACUUM */ /* @@ -11289,34 +11499,8 @@ struct VTable { }; /* -** Each SQL table is represented in memory by an instance of the -** following structure. -** -** Table.zName is the name of the table. The case of the original -** CREATE TABLE statement is stored, but case is not significant for -** comparisons. -** -** Table.nCol is the number of columns in this table. Table.aCol is a -** pointer to an array of Column structures, one for each column. -** -** If the table has an INTEGER PRIMARY KEY, then Table.iPKey is the index of -** the column that is that key. Otherwise Table.iPKey is negative. Note -** that the datatype of the PRIMARY KEY must be INTEGER for this field to -** be set. An INTEGER PRIMARY KEY is used as the rowid for each row of -** the table. If a table has no INTEGER PRIMARY KEY, then a random rowid -** is generated for each row of the table. TF_HasPrimaryKey is set if -** the table has any PRIMARY KEY, INTEGER or otherwise. -** -** Table.tnum is the page number for the root BTree page of the table in the -** database file. If Table.iDb is the index of the database table backend -** in sqlite.aDb[]. 0 is for the main database and 1 is for the file that -** holds temporary tables and indices. If TF_Ephemeral is set -** then the table is stored in a file that is automatically deleted -** when the VDBE cursor to the table is closed. In this case Table.tnum -** refers VDBE cursor number that holds the table open, not to the root -** page number. Transient tables are used to hold the results of a -** sub-query that appears instead of a real table name in the FROM clause -** of a SELECT statement. +** The schema for each SQL table and view is represented in memory +** by an instance of the following structure. */ struct Table { char *zName; /* Name of the table or view */ @@ -11328,11 +11512,11 @@ struct Table { #ifndef SQLITE_OMIT_CHECK ExprList *pCheck; /* All CHECK constraints */ #endif - LogEst nRowLogEst; /* Estimated rows in table - from sqlite_stat1 table */ - int tnum; /* Root BTree node for this table (see note above) */ - i16 iPKey; /* If not negative, use aCol[iPKey] as the primary key */ + int tnum; /* Root BTree page for this table */ + i16 iPKey; /* If not negative, use aCol[iPKey] as the rowid */ i16 nCol; /* Number of columns in this table */ u16 nRef; /* Number of pointers to this Table */ + LogEst nRowLogEst; /* Estimated rows in table - from sqlite_stat1 table */ LogEst szTabRow; /* Estimated size of each table row in bytes */ #ifdef SQLITE_ENABLE_COSTMULT LogEst costMult; /* Cost multiplier for using this table */ @@ -11354,6 +11538,12 @@ struct Table { /* ** Allowed values for Table.tabFlags. +** +** TF_OOOHidden applies to virtual tables that have hidden columns that are +** followed by non-hidden columns. Example: "CREATE VIRTUAL TABLE x USING +** vtab1(a HIDDEN, b);". Since "b" is a non-hidden column but "a" is hidden, +** the TF_OOOHidden attribute would apply in this case. Such tables require +** special handling during INSERT processing. */ #define TF_Readonly 0x01 /* Read-only system table */ #define TF_Ephemeral 0x02 /* An ephemeral table */ @@ -11361,6 +11551,7 @@ struct Table { #define TF_Autoincrement 0x08 /* Integer primary key is autoincrement */ #define TF_Virtual 0x10 /* Is a virtual table */ #define TF_WithoutRowid 0x20 /* No rowid used. PRIMARY KEY is the key */ +#define TF_OOOHidden 0x40 /* Out-of-Order hidden columns */ /* @@ -11797,8 +11988,14 @@ struct Expr { #define EP_MemToken 0x010000 /* Need to sqlite3DbFree() Expr.zToken */ #define EP_NoReduce 0x020000 /* Cannot EXPRDUP_REDUCE this Expr */ #define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */ -#define EP_Constant 0x080000 /* Node is a constant */ +#define EP_ConstFunc 0x080000 /* Node is a SQLITE_FUNC_CONSTANT function */ #define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */ +#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */ + +/* +** Combinations of two or more EP_* flags +*/ +#define EP_Propagate (EP_Collate|EP_Subquery) /* Propagate these bits up tree */ /* ** These macros can be used to test, set, or clear bits in the @@ -11997,7 +12194,7 @@ struct SrcList { #define WHERE_OMIT_OPEN_CLOSE 0x0010 /* Table cursors are already open */ #define WHERE_FORCE_TABLE 0x0020 /* Do not use an index-only search */ #define WHERE_ONETABLE_ONLY 0x0040 /* Only code the 1st table in pTabList */ - /* 0x0080 // not currently used */ +#define WHERE_NO_AUTOINDEX 0x0080 /* Disallow automatic indexes */ #define WHERE_GROUPBY 0x0100 /* pOrderBy is really a GROUP BY */ #define WHERE_DISTINCTBY 0x0200 /* pOrderby is really a DISTINCT clause */ #define WHERE_WANT_DISTINCT 0x0400 /* All output needs to be distinct */ @@ -12111,11 +12308,12 @@ struct Select { #define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */ #define SF_Compound 0x0040 /* Part of a compound query */ #define SF_Values 0x0080 /* Synthesized from VALUES clause */ -#define SF_AllValues 0x0100 /* All terms of compound are VALUES */ +#define SF_MultiValue 0x0100 /* Single VALUES term with multiple rows */ #define SF_NestedFrom 0x0200 /* Part of a parenthesized FROM clause */ #define SF_MaybeConvert 0x0400 /* Need convertCompoundSelectToSubquery() */ #define SF_Recursive 0x0800 /* The recursive part of a recursive CTE */ #define SF_MinMaxAgg 0x1000 /* Aggregate containing min() or max() */ +#define SF_Converted 0x2000 /* By convertCompoundSelectToSubquery() */ /* @@ -12434,7 +12632,8 @@ struct AuthContext { #define OPFLAG_LENGTHARG 0x40 /* OP_Column only used for length() */ #define OPFLAG_TYPEOFARG 0x80 /* OP_Column only used for typeof() */ #define OPFLAG_BULKCSR 0x01 /* OP_Open** used to open bulk cursor */ -#define OPFLAG_P2ISREG 0x02 /* P2 to OP_Open** is a register number */ +#define OPFLAG_SEEKEQ 0x02 /* OP_Open** cursor uses EQ seek only */ +#define OPFLAG_P2ISREG 0x04 /* P2 to OP_Open** is a register number */ #define OPFLAG_PERMUTE 0x01 /* OP_Compare: use the permutation */ /* @@ -12493,7 +12692,7 @@ struct Trigger { * orconf -> stores the ON CONFLICT algorithm * pSelect -> If this is an INSERT INTO ... SELECT ... statement, then * this stores a pointer to the SELECT statement. Otherwise NULL. - * target -> A token holding the quoted name of the table to insert into. + * zTarget -> Dequoted name of the table to insert into. * pExprList -> If this is an INSERT INTO ... VALUES ... statement, then * this stores values to be inserted. Otherwise NULL. * pIdList -> If this is an INSERT INTO ... () VALUES ... @@ -12501,12 +12700,12 @@ struct Trigger { * inserted into. * * (op == TK_DELETE) - * target -> A token holding the quoted name of the table to delete from. + * zTarget -> Dequoted name of the table to delete from. * pWhere -> The WHERE clause of the DELETE statement if one is specified. * Otherwise NULL. * * (op == TK_UPDATE) - * target -> A token holding the quoted name of the table to update rows of. + * zTarget -> Dequoted name of the table to update. * pWhere -> The WHERE clause of the UPDATE statement if one is specified. * Otherwise NULL. * pExprList -> A list of the columns to update and the expressions to update @@ -12518,8 +12717,8 @@ struct TriggerStep { u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */ u8 orconf; /* OE_Rollback etc. */ Trigger *pTrig; /* The trigger that this step is a part of */ - Select *pSelect; /* SELECT statment or RHS of INSERT INTO .. SELECT ... */ - Token target; /* Target table for DELETE, UPDATE, INSERT */ + Select *pSelect; /* SELECT statement or RHS of INSERT INTO SELECT ... */ + char *zTarget; /* Target table for DELETE, UPDATE, INSERT */ Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */ ExprList *pExprList; /* SET clause for UPDATE. */ IdList *pIdList; /* Column names for INSERT */ @@ -12552,8 +12751,7 @@ struct StrAccum { char *zText; /* The string collected so far */ int nChar; /* Length of the string so far */ int nAlloc; /* Amount of space allocated in zText */ - int mxAlloc; /* Maximum allowed string length */ - u8 useMalloc; /* 0: none, 1: sqlite3DbMalloc, 2: sqlite3_malloc */ + int mxAlloc; /* Maximum allowed allocation. 0 for no malloc usage */ u8 accError; /* STRACCUM_NOMEM or STRACCUM_TOOBIG */ }; #define STRACCUM_NOMEM 1 @@ -12838,10 +13036,15 @@ SQLITE_PRIVATE int sqlite3MutexInit(void); SQLITE_PRIVATE int sqlite3MutexEnd(void); #endif -SQLITE_PRIVATE int sqlite3StatusValue(int); -SQLITE_PRIVATE void sqlite3StatusAdd(int, int); +SQLITE_PRIVATE sqlite3_int64 sqlite3StatusValue(int); +SQLITE_PRIVATE void sqlite3StatusUp(int, int); +SQLITE_PRIVATE void sqlite3StatusDown(int, int); SQLITE_PRIVATE void sqlite3StatusSet(int, int); +/* Access to mutexes used by sqlite3_status() */ +SQLITE_PRIVATE sqlite3_mutex *sqlite3Pcache1Mutex(void); +SQLITE_PRIVATE sqlite3_mutex *sqlite3MallocMutex(void); + #ifndef SQLITE_OMIT_FLOATING_POINT SQLITE_PRIVATE int sqlite3IsNaN(double); #else @@ -12865,7 +13068,7 @@ SQLITE_PRIVATE void sqlite3XPrintf(StrAccum*, u32, const char*, ...); SQLITE_PRIVATE char *sqlite3MPrintf(sqlite3*,const char*, ...); SQLITE_PRIVATE char *sqlite3VMPrintf(sqlite3*,const char*, va_list); SQLITE_PRIVATE char *sqlite3MAppendf(sqlite3*,char*,const char*,...); -#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG) +#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) SQLITE_PRIVATE void sqlite3DebugPrintf(const char*, ...); #endif #if defined(SQLITE_TEST) @@ -12906,6 +13109,7 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*); SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,Token*,int); SQLITE_PRIVATE void sqlite3ExprListSetSpan(Parse*,ExprList*,ExprSpan*); SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3*, ExprList*); +SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList*); SQLITE_PRIVATE int sqlite3Init(sqlite3*, char**); SQLITE_PRIVATE int sqlite3InitCallback(void*, int, char**, char**); SQLITE_PRIVATE void sqlite3Pragma(Parse*,Token*,Token*,Token*,int); @@ -13211,7 +13415,7 @@ SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n); SQLITE_PRIVATE u8 sqlite3HexToInt(int h); SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); -#if defined(SQLITE_TEST) +#if defined(SQLITE_NEED_ERR_NAME) SQLITE_PRIVATE const char *sqlite3ErrName(int); #endif @@ -13220,7 +13424,7 @@ SQLITE_PRIVATE int sqlite3ReadSchema(Parse *pParse); SQLITE_PRIVATE CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char*,int); SQLITE_PRIVATE CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char*zName); SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr); -SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Token*); +SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Token*, int); SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse*,Expr*,const char*); SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr*); SQLITE_PRIVATE int sqlite3CheckCollSeq(Parse *, CollSeq *); @@ -13305,7 +13509,7 @@ SQLITE_PRIVATE int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *, SQLITE_PRIVATE int sqlite3ApiExit(sqlite3 *db, int); SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *); -SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum*, char*, int, int); +SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum*, sqlite3*, char*, int, int); SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum*,const char*,int); SQLITE_PRIVATE void sqlite3StrAccumAppendAll(StrAccum*,const char*); SQLITE_PRIVATE void sqlite3AppendChar(StrAccum*,int,char); @@ -13489,12 +13693,11 @@ SQLITE_PRIVATE void sqlite3MemJournalOpen(sqlite3_file *); SQLITE_PRIVATE int sqlite3MemJournalSize(void); SQLITE_PRIVATE int sqlite3IsMemJournal(sqlite3_file *); +SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p); #if SQLITE_MAX_EXPR_DEPTH>0 -SQLITE_PRIVATE void sqlite3ExprSetHeight(Parse *pParse, Expr *p); SQLITE_PRIVATE int sqlite3SelectExprHeight(Select *); SQLITE_PRIVATE int sqlite3ExprCheckHeight(Parse*, int); #else - #define sqlite3ExprSetHeight(x,y) #define sqlite3SelectExprHeight(x) 0 #define sqlite3ExprCheckHeight(x,y) #endif @@ -13524,7 +13727,7 @@ SQLITE_PRIVATE void sqlite3ParserTrace(FILE*, char *); #ifdef SQLITE_ENABLE_IOTRACE # define IOTRACE(A) if( sqlite3IoTrace ){ sqlite3IoTrace A; } SQLITE_PRIVATE void sqlite3VdbeIOTraceSql(Vdbe*); -void (*sqlite3IoTrace)(const char*,...); +SQLITE_API SQLITE_EXTERN void (SQLITE_CDECL *sqlite3IoTrace)(const char*,...); #else # define IOTRACE(A) # define sqlite3VdbeIOTraceSql(X) @@ -13631,16 +13834,16 @@ SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[] = { 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 3x */ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 4x */ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 5x */ - 96, 97, 66, 67, 68, 69, 70, 71, 72, 73,106,107,108,109,110,111, /* 6x */ - 112, 81, 82, 83, 84, 85, 86, 87, 88, 89,122,123,124,125,126,127, /* 7x */ + 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, /* 6x */ + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, /* 7x */ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 8x */ - 144,145,146,147,148,149,150,151,152,153,154,155,156,157,156,159, /* 9x */ + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, /* 9x */ 160,161,162,163,164,165,166,167,168,169,170,171,140,141,142,175, /* Ax */ 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, /* Bx */ 192,129,130,131,132,133,134,135,136,137,202,203,204,205,206,207, /* Cx */ 208,145,146,147,148,149,150,151,152,153,218,219,220,221,222,223, /* Dx */ - 224,225,162,163,164,165,166,167,168,169,232,203,204,205,206,207, /* Ex */ - 239,240,241,242,243,244,245,246,247,248,249,219,220,221,222,255, /* Fx */ + 224,225,162,163,164,165,166,167,168,169,234,235,236,237,238,239, /* Ex */ + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255, /* Fx */ #endif }; @@ -14237,7 +14440,7 @@ static const char * const azCompileOpt[] = { ** The name can optionally begin with "SQLITE_" but the "SQLITE_" prefix ** is not required for a match. */ -SQLITE_API int sqlite3_compileoption_used(const char *zOptName){ +SQLITE_API int SQLITE_STDCALL sqlite3_compileoption_used(const char *zOptName){ int i, n; #if SQLITE_ENABLE_API_ARMOR @@ -14265,7 +14468,7 @@ SQLITE_API int sqlite3_compileoption_used(const char *zOptName){ ** Return the N-th compile-time option string. If N is out of range, ** return a NULL pointer. */ -SQLITE_API const char *sqlite3_compileoption_get(int N){ +SQLITE_API const char *SQLITE_STDCALL sqlite3_compileoption_get(int N){ if( N>=0 && N4 + sqlite3_int64 nowValue[10]; /* Current value */ + sqlite3_int64 mxValue[10]; /* Maximum value */ +#else + u32 nowValue[10]; /* Current value */ + u32 mxValue[10]; /* Maximum value */ +#endif } sqlite3Stat = { {0,}, {0,} }; +/* +** Elements of sqlite3Stat[] are protected by either the memory allocator +** mutex, or by the pcache1 mutex. The following array determines which. +*/ +static const char statMutex[] = { + 0, /* SQLITE_STATUS_MEMORY_USED */ + 1, /* SQLITE_STATUS_PAGECACHE_USED */ + 1, /* SQLITE_STATUS_PAGECACHE_OVERFLOW */ + 0, /* SQLITE_STATUS_SCRATCH_USED */ + 0, /* SQLITE_STATUS_SCRATCH_OVERFLOW */ + 0, /* SQLITE_STATUS_MALLOC_SIZE */ + 0, /* SQLITE_STATUS_PARSER_STACK */ + 1, /* SQLITE_STATUS_PAGECACHE_SIZE */ + 0, /* SQLITE_STATUS_SCRATCH_SIZE */ + 0, /* SQLITE_STATUS_MALLOC_COUNT */ +}; + /* The "wsdStat" macro will resolve to the status information ** state vector. If writable static data is unsupported on the target, @@ -14823,33 +15042,60 @@ static SQLITE_WSD struct sqlite3StatType { #endif /* -** Return the current value of a status parameter. +** Return the current value of a status parameter. The caller must +** be holding the appropriate mutex. */ -SQLITE_PRIVATE int sqlite3StatusValue(int op){ +SQLITE_PRIVATE sqlite3_int64 sqlite3StatusValue(int op){ wsdStatInit; assert( op>=0 && op=0 && op=0 && op=0 && opwsdStat.mxValue[op] ){ wsdStat.mxValue[op] = wsdStat.nowValue[op]; } } +SQLITE_PRIVATE void sqlite3StatusDown(int op, int N){ + wsdStatInit; + assert( N>=0 ); + assert( op>=0 && op=0 && op=0 && op=0 && opwsdStat.mxValue[op] ){ wsdStat.mxValue[op] = wsdStat.nowValue[op]; @@ -14858,12 +15104,14 @@ SQLITE_PRIVATE void sqlite3StatusSet(int op, int X){ /* ** Query status information. -** -** This implementation assumes that reading or writing an aligned -** 32-bit integer is an atomic operation. If that assumption is not true, -** then this routine is not threadsafe. */ -SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag){ +SQLITE_API int SQLITE_STDCALL sqlite3_status64( + int op, + sqlite3_int64 *pCurrent, + sqlite3_int64 *pHighwater, + int resetFlag +){ + sqlite3_mutex *pMutex; wsdStatInit; if( op<0 || op>=ArraySize(wsdStat.nowValue) ){ return SQLITE_MISUSE_BKPT; @@ -14871,18 +15119,35 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF #ifdef SQLITE_ENABLE_API_ARMOR if( pCurrent==0 || pHighwater==0 ) return SQLITE_MISUSE_BKPT; #endif + pMutex = statMutex[op] ? sqlite3Pcache1Mutex() : sqlite3MallocMutex(); + sqlite3_mutex_enter(pMutex); *pCurrent = wsdStat.nowValue[op]; *pHighwater = wsdStat.mxValue[op]; if( resetFlag ){ wsdStat.mxValue[op] = wsdStat.nowValue[op]; } + sqlite3_mutex_leave(pMutex); + (void)pMutex; /* Prevent warning when SQLITE_THREADSAFE=0 */ return SQLITE_OK; } +SQLITE_API int SQLITE_STDCALL sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag){ + sqlite3_int64 iCur, iHwtr; + int rc; +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCurrent==0 || pHighwater==0 ) return SQLITE_MISUSE_BKPT; +#endif + rc = sqlite3_status64(op, &iCur, &iHwtr, resetFlag); + if( rc==0 ){ + *pCurrent = (int)iCur; + *pHighwater = (int)iHwtr; + } + return rc; +} /* ** Query status information for a single database connection */ -SQLITE_API int sqlite3_db_status( +SQLITE_API int SQLITE_STDCALL sqlite3_db_status( sqlite3 *db, /* The database connection whose status is desired */ int op, /* Status verb */ int *pCurrent, /* Write current value here */ @@ -16506,7 +16771,7 @@ static sqlite3_vfs * SQLITE_WSD vfsList = 0; ** Locate a VFS by name. If no name is given, simply return the ** first VFS on the list. */ -SQLITE_API sqlite3_vfs *sqlite3_vfs_find(const char *zVfs){ +SQLITE_API sqlite3_vfs *SQLITE_STDCALL sqlite3_vfs_find(const char *zVfs){ sqlite3_vfs *pVfs = 0; #if SQLITE_THREADSAFE sqlite3_mutex *mutex; @@ -16552,7 +16817,7 @@ static void vfsUnlink(sqlite3_vfs *pVfs){ ** VFS multiple times. The new VFS becomes the default if makeDflt is ** true. */ -SQLITE_API int sqlite3_vfs_register(sqlite3_vfs *pVfs, int makeDflt){ +SQLITE_API int SQLITE_STDCALL sqlite3_vfs_register(sqlite3_vfs *pVfs, int makeDflt){ MUTEX_LOGIC(sqlite3_mutex *mutex;) #ifndef SQLITE_OMIT_AUTOINIT int rc = sqlite3_initialize(); @@ -16580,7 +16845,7 @@ SQLITE_API int sqlite3_vfs_register(sqlite3_vfs *pVfs, int makeDflt){ /* ** Unregister a VFS so that it is no longer accessible. */ -SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs *pVfs){ +SQLITE_API int SQLITE_STDCALL sqlite3_vfs_unregister(sqlite3_vfs *pVfs){ #if SQLITE_THREADSAFE sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); #endif @@ -18916,7 +19181,7 @@ SQLITE_PRIVATE int sqlite3MutexEnd(void){ /* ** Retrieve a pointer to a static mutex or allocate a new dynamic one. */ -SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int id){ +SQLITE_API sqlite3_mutex *SQLITE_STDCALL sqlite3_mutex_alloc(int id){ #ifndef SQLITE_OMIT_AUTOINIT if( id<=SQLITE_MUTEX_RECURSIVE && sqlite3_initialize() ) return 0; if( id>SQLITE_MUTEX_RECURSIVE && sqlite3MutexInit() ) return 0; @@ -18935,7 +19200,7 @@ SQLITE_PRIVATE sqlite3_mutex *sqlite3MutexAlloc(int id){ /* ** Free a dynamic mutex. */ -SQLITE_API void sqlite3_mutex_free(sqlite3_mutex *p){ +SQLITE_API void SQLITE_STDCALL sqlite3_mutex_free(sqlite3_mutex *p){ if( p ){ sqlite3GlobalConfig.mutex.xMutexFree(p); } @@ -18945,7 +19210,7 @@ SQLITE_API void sqlite3_mutex_free(sqlite3_mutex *p){ ** Obtain the mutex p. If some other thread already has the mutex, block ** until it can be obtained. */ -SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex *p){ +SQLITE_API void SQLITE_STDCALL sqlite3_mutex_enter(sqlite3_mutex *p){ if( p ){ sqlite3GlobalConfig.mutex.xMutexEnter(p); } @@ -18955,7 +19220,7 @@ SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex *p){ ** Obtain the mutex p. If successful, return SQLITE_OK. Otherwise, if another ** thread holds the mutex and it cannot be obtained, return SQLITE_BUSY. */ -SQLITE_API int sqlite3_mutex_try(sqlite3_mutex *p){ +SQLITE_API int SQLITE_STDCALL sqlite3_mutex_try(sqlite3_mutex *p){ int rc = SQLITE_OK; if( p ){ return sqlite3GlobalConfig.mutex.xMutexTry(p); @@ -18969,7 +19234,7 @@ SQLITE_API int sqlite3_mutex_try(sqlite3_mutex *p){ ** is not currently entered. If a NULL pointer is passed as an argument ** this function is a no-op. */ -SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex *p){ +SQLITE_API void SQLITE_STDCALL sqlite3_mutex_leave(sqlite3_mutex *p){ if( p ){ sqlite3GlobalConfig.mutex.xMutexLeave(p); } @@ -18980,10 +19245,10 @@ SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex *p){ ** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are ** intended for use inside assert() statements. */ -SQLITE_API int sqlite3_mutex_held(sqlite3_mutex *p){ +SQLITE_API int SQLITE_STDCALL sqlite3_mutex_held(sqlite3_mutex *p){ return p==0 || sqlite3GlobalConfig.mutex.xMutexHeld(p); } -SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex *p){ +SQLITE_API int SQLITE_STDCALL sqlite3_mutex_notheld(sqlite3_mutex *p){ return p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld(p); } #endif @@ -19113,8 +19378,12 @@ static sqlite3_mutex *debugMutexAlloc(int id){ break; } default: { - assert( id-2 >= 0 ); - assert( id-2 < (int)(sizeof(aStatic)/sizeof(aStatic[0])) ); +#ifdef SQLITE_ENABLE_API_ARMOR + if( id-2<0 || id-2>=ArraySize(aStatic) ){ + (void)SQLITE_MISUSE_BKPT; + return 0; + } +#endif pNew = &aStatic[id-2]; pNew->id = id; break; @@ -19129,8 +19398,13 @@ static sqlite3_mutex *debugMutexAlloc(int id){ static void debugMutexFree(sqlite3_mutex *pX){ sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX; assert( p->cnt==0 ); - assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ); - sqlite3_free(p); + if( p->id==SQLITE_MUTEX_RECURSIVE || p->id==SQLITE_MUTEX_FAST ){ + sqlite3_free(p); + }else{ +#ifdef SQLITE_ENABLE_API_ARMOR + (void)SQLITE_MISUSE_BKPT; +#endif + } } /* @@ -19241,8 +19515,10 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ */ struct sqlite3_mutex { pthread_mutex_t mutex; /* Mutex controlling the lock */ -#if SQLITE_MUTEX_NREF +#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR) int id; /* Mutex type */ +#endif +#if SQLITE_MUTEX_NREF volatile int nRef; /* Number of entrances */ volatile pthread_t owner; /* Thread that is within this mutex */ int trace; /* True to trace changes */ @@ -19358,9 +19634,6 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){ pthread_mutexattr_settype(&recursiveAttr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&p->mutex, &recursiveAttr); pthread_mutexattr_destroy(&recursiveAttr); -#endif -#if SQLITE_MUTEX_NREF - p->id = iType; #endif } break; @@ -19368,9 +19641,6 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){ case SQLITE_MUTEX_FAST: { p = sqlite3MallocZero( sizeof(*p) ); if( p ){ -#if SQLITE_MUTEX_NREF - p->id = iType; -#endif pthread_mutex_init(&p->mutex, 0); } break; @@ -19383,12 +19653,12 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){ } #endif p = &staticMutexes[iType-2]; -#if SQLITE_MUTEX_NREF - p->id = iType; -#endif break; } } +#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR) + if( p ) p->id = iType; +#endif return p; } @@ -19400,9 +19670,18 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){ */ static void pthreadMutexFree(sqlite3_mutex *p){ assert( p->nRef==0 ); - assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ); - pthread_mutex_destroy(&p->mutex); - sqlite3_free(p); +#if SQLITE_ENABLE_API_ARMOR + if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ) +#endif + { + pthread_mutex_destroy(&p->mutex); + sqlite3_free(p); + } +#ifdef SQLITE_ENABLE_API_ARMOR + else{ + (void)SQLITE_MISUSE_BKPT; + } +#endif } /* @@ -19614,16 +19893,6 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ # error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." #endif -#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) -# ifndef SQLITE_DEBUG_OS_TRACE -# define SQLITE_DEBUG_OS_TRACE 0 -# endif - int sqlite3OSTrace = SQLITE_DEBUG_OS_TRACE; -# define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X -#else -# define OSTRACE(X) -#endif - /* ** Macros for performance tracing. Normally turned off. Only works ** on i486 hardware. @@ -19872,6 +20141,17 @@ SQLITE_API int sqlite3_open_file_count = 0; # define SQLITE_WIN32_VOLATILE volatile #endif +/* +** For some Windows sub-platforms, the _beginthreadex() / _endthreadex() +** functions are not available (e.g. those not using MSVC, Cygwin, etc). +*/ +#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \ + SQLITE_THREADSAFE>0 && !defined(__CYGWIN__) +# define SQLITE_OS_WIN_THREADS 1 +#else +# define SQLITE_OS_WIN_THREADS 0 +#endif + #endif /* _OS_WIN_H_ */ /************** End of os_win.h **********************************************/ @@ -19954,8 +20234,8 @@ static int winMutex_isNt = -1; /* <0 means "need to query" */ */ static LONG SQLITE_WIN32_VOLATILE winMutex_lock = 0; -SQLITE_API int sqlite3_win32_is_nt(void); /* os_win.c */ -SQLITE_API void sqlite3_win32_sleep(DWORD milliseconds); /* os_win.c */ +SQLITE_API int SQLITE_STDCALL sqlite3_win32_is_nt(void); /* os_win.c */ +SQLITE_API void SQLITE_STDCALL sqlite3_win32_sleep(DWORD milliseconds); /* os_win.c */ static int winMutexInit(void){ /* The first to increment to 1 does actual initialization */ @@ -20047,8 +20327,8 @@ static sqlite3_mutex *winMutexAlloc(int iType){ case SQLITE_MUTEX_RECURSIVE: { p = sqlite3MallocZero( sizeof(*p) ); if( p ){ -#ifdef SQLITE_DEBUG p->id = iType; +#ifdef SQLITE_DEBUG #ifdef SQLITE_WIN32_MUTEX_TRACE_DYNAMIC p->trace = 1; #endif @@ -20068,12 +20348,9 @@ static sqlite3_mutex *winMutexAlloc(int iType){ return 0; } #endif - assert( iType-2 >= 0 ); - assert( iType-2 < ArraySize(winMutex_staticMutexes) ); - assert( winMutex_isInit==1 ); p = &winMutex_staticMutexes[iType-2]; -#ifdef SQLITE_DEBUG p->id = iType; +#ifdef SQLITE_DEBUG #ifdef SQLITE_WIN32_MUTEX_TRACE_STATIC p->trace = 1; #endif @@ -20092,13 +20369,15 @@ static sqlite3_mutex *winMutexAlloc(int iType){ */ static void winMutexFree(sqlite3_mutex *p){ assert( p ); -#ifdef SQLITE_DEBUG assert( p->nRef==0 && p->owner==0 ); - assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ); + if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ){ + DeleteCriticalSection(&p->mutex); + sqlite3_free(p); + }else{ +#ifdef SQLITE_ENABLE_API_ARMOR + (void)SQLITE_MISUSE_BKPT; #endif - assert( winMutex_isInit==1 ); - DeleteCriticalSection(&p->mutex); - sqlite3_free(p); + } } /* @@ -20252,7 +20531,7 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ ** held by SQLite. An example of non-essential memory is memory used to ** cache database pages that are not currently in use. */ -SQLITE_API int sqlite3_release_memory(int n){ +SQLITE_API int SQLITE_STDCALL sqlite3_release_memory(int n){ #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT return sqlite3PcacheReleaseMemory(n); #else @@ -20307,6 +20586,13 @@ static SQLITE_WSD struct Mem0Global { #define mem0 GLOBAL(struct Mem0Global, mem0) +/* +** Return the memory allocator mutex. sqlite3_status() needs it. +*/ +SQLITE_PRIVATE sqlite3_mutex *sqlite3MallocMutex(void){ + return mem0.mutex; +} + /* ** This routine runs when the memory allocator sees that the ** total memory allocation is about to exceed the soft heap @@ -20329,7 +20615,7 @@ static int sqlite3MemoryAlarm( void *pArg, sqlite3_int64 iThreshold ){ - int nUsed; + sqlite3_int64 nUsed; sqlite3_mutex_enter(mem0.mutex); mem0.alarmCallback = xCallback; mem0.alarmArg = pArg; @@ -20345,7 +20631,7 @@ static int sqlite3MemoryAlarm( ** Deprecated external interface. Internal/core SQLite code ** should call sqlite3MemoryAlarm. */ -SQLITE_API int sqlite3_memory_alarm( +SQLITE_API int SQLITE_STDCALL sqlite3_memory_alarm( void(*xCallback)(void *pArg, sqlite3_int64 used,int N), void *pArg, sqlite3_int64 iThreshold @@ -20358,7 +20644,7 @@ SQLITE_API int sqlite3_memory_alarm( ** Set the soft heap-size limit for the library. Passing a zero or ** negative value indicates no limit. */ -SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 n){ +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_soft_heap_limit64(sqlite3_int64 n){ sqlite3_int64 priorLimit; sqlite3_int64 excess; #ifndef SQLITE_OMIT_AUTOINIT @@ -20378,7 +20664,7 @@ SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 n){ if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff)); return priorLimit; } -SQLITE_API void sqlite3_soft_heap_limit(int n){ +SQLITE_API void SQLITE_STDCALL sqlite3_soft_heap_limit(int n){ if( n<0 ) n = 0; sqlite3_soft_heap_limit64(n); } @@ -20387,6 +20673,7 @@ SQLITE_API void sqlite3_soft_heap_limit(int n){ ** Initialize the memory allocation subsystem. */ SQLITE_PRIVATE int sqlite3MallocInit(void){ + int rc; if( sqlite3GlobalConfig.m.xMalloc==0 ){ sqlite3MemSetDefault(); } @@ -20422,7 +20709,9 @@ SQLITE_PRIVATE int sqlite3MallocInit(void){ sqlite3GlobalConfig.szPage = 0; sqlite3GlobalConfig.nPage = 0; } - return sqlite3GlobalConfig.m.xInit(sqlite3GlobalConfig.m.pAppData); + rc = sqlite3GlobalConfig.m.xInit(sqlite3GlobalConfig.m.pAppData); + if( rc!=SQLITE_OK ) memset(&mem0, 0, sizeof(mem0)); + return rc; } /* @@ -20447,7 +20736,7 @@ SQLITE_PRIVATE void sqlite3MallocEnd(void){ /* ** Return the amount of memory currently checked out. */ -SQLITE_API sqlite3_int64 sqlite3_memory_used(void){ +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_memory_used(void){ int n, mx; sqlite3_int64 res; sqlite3_status(SQLITE_STATUS_MEMORY_USED, &n, &mx, 0); @@ -20460,7 +20749,7 @@ SQLITE_API sqlite3_int64 sqlite3_memory_used(void){ ** checked out since either the beginning of this process ** or since the most recent reset. */ -SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag){ +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_memory_highwater(int resetFlag){ int n, mx; sqlite3_int64 res; sqlite3_status(SQLITE_STATUS_MEMORY_USED, &n, &mx, resetFlag); @@ -20498,7 +20787,7 @@ static int mallocWithAlarm(int n, void **pp){ nFull = sqlite3GlobalConfig.m.xRoundup(n); sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, n); if( mem0.alarmCallback!=0 ){ - int nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); + sqlite3_int64 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); if( nUsed >= mem0.alarmThreshold - nFull ){ mem0.nearlyFull = 1; sqlite3MallocAlarm(nFull); @@ -20515,8 +20804,8 @@ static int mallocWithAlarm(int n, void **pp){ #endif if( p ){ nFull = sqlite3MallocSize(p); - sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nFull); - sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, 1); + sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nFull); + sqlite3StatusUp(SQLITE_STATUS_MALLOC_COUNT, 1); } *pp = p; return nFull; @@ -20551,13 +20840,13 @@ SQLITE_PRIVATE void *sqlite3Malloc(u64 n){ ** First make sure the memory subsystem is initialized, then do the ** allocation. */ -SQLITE_API void *sqlite3_malloc(int n){ +SQLITE_API void *SQLITE_STDCALL sqlite3_malloc(int n){ #ifndef SQLITE_OMIT_AUTOINIT if( sqlite3_initialize() ) return 0; #endif return n<=0 ? 0 : sqlite3Malloc(n); } -SQLITE_API void *sqlite3_malloc64(sqlite3_uint64 n){ +SQLITE_API void *SQLITE_STDCALL sqlite3_malloc64(sqlite3_uint64 n){ #ifndef SQLITE_OMIT_AUTOINIT if( sqlite3_initialize() ) return 0; #endif @@ -20593,14 +20882,14 @@ SQLITE_PRIVATE void *sqlite3ScratchMalloc(int n){ p = mem0.pScratchFree; mem0.pScratchFree = mem0.pScratchFree->pNext; mem0.nScratchFree--; - sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, 1); + sqlite3StatusUp(SQLITE_STATUS_SCRATCH_USED, 1); sqlite3_mutex_leave(mem0.mutex); }else{ sqlite3_mutex_leave(mem0.mutex); p = sqlite3Malloc(n); if( sqlite3GlobalConfig.bMemstat && p ){ sqlite3_mutex_enter(mem0.mutex); - sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, sqlite3MallocSize(p)); + sqlite3StatusUp(SQLITE_STATUS_SCRATCH_OVERFLOW, sqlite3MallocSize(p)); sqlite3_mutex_leave(mem0.mutex); } sqlite3MemdebugSetType(p, MEMTYPE_SCRATCH); @@ -20641,19 +20930,19 @@ SQLITE_PRIVATE void sqlite3ScratchFree(void *p){ mem0.pScratchFree = pSlot; mem0.nScratchFree++; assert( mem0.nScratchFree <= (u32)sqlite3GlobalConfig.nScratch ); - sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, -1); + sqlite3StatusDown(SQLITE_STATUS_SCRATCH_USED, 1); sqlite3_mutex_leave(mem0.mutex); }else{ /* Release memory back to the heap */ assert( sqlite3MemdebugHasType(p, MEMTYPE_SCRATCH) ); - assert( sqlite3MemdebugNoType(p, ~MEMTYPE_SCRATCH) ); + assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_SCRATCH) ); sqlite3MemdebugSetType(p, MEMTYPE_HEAP); if( sqlite3GlobalConfig.bMemstat ){ int iSize = sqlite3MallocSize(p); sqlite3_mutex_enter(mem0.mutex); - sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, -iSize); - sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -iSize); - sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, -1); + sqlite3StatusDown(SQLITE_STATUS_SCRATCH_OVERFLOW, iSize); + sqlite3StatusDown(SQLITE_STATUS_MEMORY_USED, iSize); + sqlite3StatusDown(SQLITE_STATUS_MALLOC_COUNT, 1); sqlite3GlobalConfig.m.xFree(p); sqlite3_mutex_leave(mem0.mutex); }else{ @@ -20684,7 +20973,7 @@ SQLITE_PRIVATE int sqlite3MallocSize(void *p){ } SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, void *p){ if( db==0 ){ - assert( sqlite3MemdebugNoType(p, ~MEMTYPE_HEAP) ); + assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); return sqlite3MallocSize(p); }else{ @@ -20693,13 +20982,13 @@ SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, void *p){ return db->lookaside.sz; }else{ assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); - assert( sqlite3MemdebugNoType(p, ~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); + assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); return sqlite3GlobalConfig.m.xSize(p); } } } -SQLITE_API sqlite3_uint64 sqlite3_msize(void *p){ - assert( sqlite3MemdebugNoType(p, ~MEMTYPE_HEAP) ); +SQLITE_API sqlite3_uint64 SQLITE_STDCALL sqlite3_msize(void *p){ + assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); return (sqlite3_uint64)sqlite3GlobalConfig.m.xSize(p); } @@ -20707,14 +20996,14 @@ SQLITE_API sqlite3_uint64 sqlite3_msize(void *p){ /* ** Free memory previously obtained from sqlite3Malloc(). */ -SQLITE_API void sqlite3_free(void *p){ +SQLITE_API void SQLITE_STDCALL sqlite3_free(void *p){ if( p==0 ) return; /* IMP: R-49053-54554 */ assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); - assert( sqlite3MemdebugNoType(p, ~MEMTYPE_HEAP) ); + assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); if( sqlite3GlobalConfig.bMemstat ){ sqlite3_mutex_enter(mem0.mutex); - sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -sqlite3MallocSize(p)); - sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, -1); + sqlite3StatusDown(SQLITE_STATUS_MEMORY_USED, sqlite3MallocSize(p)); + sqlite3StatusDown(SQLITE_STATUS_MALLOC_COUNT, 1); sqlite3GlobalConfig.m.xFree(p); sqlite3_mutex_leave(mem0.mutex); }else{ @@ -20755,7 +21044,7 @@ SQLITE_PRIVATE void sqlite3DbFree(sqlite3 *db, void *p){ } } assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); - assert( sqlite3MemdebugNoType(p, ~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); + assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); assert( db!=0 || sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) ); sqlite3MemdebugSetType(p, MEMTYPE_HEAP); sqlite3_free(p); @@ -20768,7 +21057,7 @@ SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, u64 nBytes){ int nOld, nNew, nDiff; void *pNew; assert( sqlite3MemdebugHasType(pOld, MEMTYPE_HEAP) ); - assert( sqlite3MemdebugNoType(pOld, ~MEMTYPE_HEAP) ); + assert( sqlite3MemdebugNoType(pOld, (u8)~MEMTYPE_HEAP) ); if( pOld==0 ){ return sqlite3Malloc(nBytes); /* IMP: R-04300-56712 */ } @@ -20802,7 +21091,7 @@ SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, u64 nBytes){ } if( pNew ){ nNew = sqlite3MallocSize(pNew); - sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nNew-nOld); + sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nNew-nOld); } sqlite3_mutex_leave(mem0.mutex); }else{ @@ -20816,14 +21105,14 @@ SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, u64 nBytes){ ** The public interface to sqlite3Realloc. Make sure that the memory ** subsystem is initialized prior to invoking sqliteRealloc. */ -SQLITE_API void *sqlite3_realloc(void *pOld, int n){ +SQLITE_API void *SQLITE_STDCALL sqlite3_realloc(void *pOld, int n){ #ifndef SQLITE_OMIT_AUTOINIT if( sqlite3_initialize() ) return 0; #endif if( n<0 ) n = 0; /* IMP: R-26507-47431 */ return sqlite3Realloc(pOld, n); } -SQLITE_API void *sqlite3_realloc64(void *pOld, sqlite3_uint64 n){ +SQLITE_API void *SQLITE_STDCALL sqlite3_realloc64(void *pOld, sqlite3_uint64 n){ #ifndef SQLITE_OMIT_AUTOINIT if( sqlite3_initialize() ) return 0; #endif @@ -20935,7 +21224,7 @@ SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *db, void *p, u64 n){ } }else{ assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); - assert( sqlite3MemdebugNoType(p, ~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); + assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); sqlite3MemdebugSetType(p, MEMTYPE_HEAP); pNew = sqlite3_realloc64(p, n); if( !pNew ){ @@ -21188,6 +21477,7 @@ static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){ ** Set the StrAccum object to an error mode. */ static void setStrAccumError(StrAccum *p, u8 eError){ + assert( eError==STRACCUM_NOMEM || eError==STRACCUM_TOOBIG ); p->accError = eError; p->nAlloc = 0; } @@ -21262,13 +21552,6 @@ SQLITE_PRIVATE void sqlite3VXPrintf( PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */ char buf[etBUFSIZE]; /* Conversion buffer */ -#ifdef SQLITE_ENABLE_API_ARMOR - if( ap==0 ){ - (void)SQLITE_MISUSE_BKPT; - sqlite3StrAccumReset(pAccum); - return; - } -#endif bufpt = 0; if( bFlags ){ if( (bArgList = (bFlags & SQLITE_PRINTF_SQLFUNC))!=0 ){ @@ -21309,7 +21592,6 @@ SQLITE_PRIVATE void sqlite3VXPrintf( } }while( !done && (c=(*++fmt))!=0 ); /* Get the field width */ - width = 0; if( c=='*' ){ if( bArgList ){ width = (int)getIntArg(pArgList); @@ -21318,18 +21600,21 @@ SQLITE_PRIVATE void sqlite3VXPrintf( } if( width<0 ){ flag_leftjustify = 1; - width = -width; + width = width >= -2147483647 ? -width : 0; } c = *++fmt; }else{ + unsigned wx = 0; while( c>='0' && c<='9' ){ - width = width*10 + c - '0'; + wx = wx*10 + c - '0'; c = *++fmt; } + testcase( wx>0x7fffffff ); + width = wx & 0x7fffffff; } + /* Get the precision */ if( c=='.' ){ - precision = 0; c = *++fmt; if( c=='*' ){ if( bArgList ){ @@ -21337,13 +21622,18 @@ SQLITE_PRIVATE void sqlite3VXPrintf( }else{ precision = va_arg(ap,int); } - if( precision<0 ) precision = -precision; c = *++fmt; + if( precision<0 ){ + precision = precision >= -2147483647 ? -precision : -1; + } }else{ + unsigned px = 0; while( c>='0' && c<='9' ){ - precision = precision*10 + c - '0'; + px = px*10 + c - '0'; c = *++fmt; } + testcase( px>0x7fffffff ); + precision = px & 0x7fffffff; } }else{ precision = -1; @@ -21507,7 +21797,8 @@ SQLITE_PRIVATE void sqlite3VXPrintf( else prefix = 0; } if( xtype==etGENERIC && precision>0 ) precision--; - for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1){} + testcase( precision>0xfff ); + for(idx=precision&0xfff, rounder=0.5; idx>0; idx--, rounder*=0.1){} if( xtype==etFLOAT ) realvalue += rounder; /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ exp = 0; @@ -21562,8 +21853,9 @@ SQLITE_PRIVATE void sqlite3VXPrintf( }else{ e2 = exp; } - if( MAX(e2,0)+precision+width > etBUFSIZE - 15 ){ - bufpt = zExtra = sqlite3Malloc( MAX(e2,0)+precision+width+15 ); + if( MAX(e2,0)+(i64)precision+(i64)width > etBUFSIZE - 15 ){ + bufpt = zExtra + = sqlite3Malloc( MAX(e2,0)+(i64)precision+(i64)width+15 ); if( bufpt==0 ){ setStrAccumError(pAccum, STRACCUM_NOMEM); return; @@ -21795,13 +22087,13 @@ SQLITE_PRIVATE void sqlite3VXPrintf( */ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){ char *zNew; - assert( p->nChar+N >= p->nAlloc ); /* Only called if really needed */ + assert( p->nChar+(i64)N >= p->nAlloc ); /* Only called if really needed */ if( p->accError ){ testcase(p->accError==STRACCUM_TOOBIG); testcase(p->accError==STRACCUM_NOMEM); return 0; } - if( !p->useMalloc ){ + if( p->mxAlloc==0 ){ N = p->nAlloc - p->nChar - 1; setStrAccumError(p, STRACCUM_TOOBIG); return N; @@ -21821,10 +22113,10 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){ }else{ p->nAlloc = (int)szNew; } - if( p->useMalloc==1 ){ + if( p->db ){ zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc); }else{ - zNew = sqlite3_realloc(zOld, p->nAlloc); + zNew = sqlite3_realloc64(zOld, p->nAlloc); } if( zNew ){ assert( p->zText!=0 || p->nChar==0 ); @@ -21844,7 +22136,10 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){ ** Append N copies of character c to the given string buffer. */ SQLITE_PRIVATE void sqlite3AppendChar(StrAccum *p, int N, char c){ - if( p->nChar+N >= p->nAlloc && (N = sqlite3StrAccumEnlarge(p, N))<=0 ) return; + testcase( p->nChar + (i64)N > 0x7fffffff ); + if( p->nChar+(i64)N >= p->nAlloc && (N = sqlite3StrAccumEnlarge(p, N))<=0 ){ + return; + } while( (N--)>0 ) p->zText[p->nChar++] = c; } @@ -21869,7 +22164,7 @@ static void SQLITE_NOINLINE enlargeAndAppend(StrAccum *p, const char *z, int N){ ** size of the memory allocation for StrAccum if necessary. */ SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){ - assert( z!=0 ); + assert( z!=0 || N==0 ); assert( p->zText!=0 || p->nChar==0 || p->accError ); assert( N>=0 ); assert( p->accError==0 || p->nAlloc==0 ); @@ -21898,12 +22193,8 @@ SQLITE_PRIVATE void sqlite3StrAccumAppendAll(StrAccum *p, const char *z){ SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum *p){ if( p->zText ){ p->zText[p->nChar] = 0; - if( p->useMalloc && p->zText==p->zBase ){ - if( p->useMalloc==1 ){ - p->zText = sqlite3DbMallocRaw(p->db, p->nChar+1 ); - }else{ - p->zText = sqlite3_malloc(p->nChar+1); - } + if( p->mxAlloc>0 && p->zText==p->zBase ){ + p->zText = sqlite3DbMallocRaw(p->db, p->nChar+1 ); if( p->zText ){ memcpy(p->zText, p->zBase, p->nChar+1); }else{ @@ -21919,25 +22210,31 @@ SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum *p){ */ SQLITE_PRIVATE void sqlite3StrAccumReset(StrAccum *p){ if( p->zText!=p->zBase ){ - if( p->useMalloc==1 ){ - sqlite3DbFree(p->db, p->zText); - }else{ - sqlite3_free(p->zText); - } + sqlite3DbFree(p->db, p->zText); } p->zText = 0; } /* -** Initialize a string accumulator +** Initialize a string accumulator. +** +** p: The accumulator to be initialized. +** db: Pointer to a database connection. May be NULL. Lookaside +** memory is used if not NULL. db->mallocFailed is set appropriately +** when not NULL. +** zBase: An initial buffer. May be NULL in which case the initial buffer +** is malloced. +** n: Size of zBase in bytes. If total space requirements never exceed +** n then no memory allocations ever occur. +** mx: Maximum number of bytes to accumulate. If mx==0 then no memory +** allocations will ever occur. */ -SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum *p, char *zBase, int n, int mx){ +SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum *p, sqlite3 *db, char *zBase, int n, int mx){ p->zText = p->zBase = zBase; - p->db = 0; + p->db = db; p->nChar = 0; p->nAlloc = n; p->mxAlloc = mx; - p->useMalloc = 1; p->accError = 0; } @@ -21950,9 +22247,8 @@ SQLITE_PRIVATE char *sqlite3VMPrintf(sqlite3 *db, const char *zFormat, va_list a char zBase[SQLITE_PRINT_BUF_SIZE]; StrAccum acc; assert( db!=0 ); - sqlite3StrAccumInit(&acc, zBase, sizeof(zBase), + sqlite3StrAccumInit(&acc, db, zBase, sizeof(zBase), db->aLimit[SQLITE_LIMIT_LENGTH]); - acc.db = db; sqlite3VXPrintf(&acc, SQLITE_PRINTF_INTERNAL, zFormat, ap); z = sqlite3StrAccumFinish(&acc); if( acc.accError==STRACCUM_NOMEM ){ @@ -21996,7 +22292,7 @@ SQLITE_PRIVATE char *sqlite3MAppendf(sqlite3 *db, char *zStr, const char *zForma ** Print into memory obtained from sqlite3_malloc(). Omit the internal ** %-conversion extensions. */ -SQLITE_API char *sqlite3_vmprintf(const char *zFormat, va_list ap){ +SQLITE_API char *SQLITE_STDCALL sqlite3_vmprintf(const char *zFormat, va_list ap){ char *z; char zBase[SQLITE_PRINT_BUF_SIZE]; StrAccum acc; @@ -22010,8 +22306,7 @@ SQLITE_API char *sqlite3_vmprintf(const char *zFormat, va_list ap){ #ifndef SQLITE_OMIT_AUTOINIT if( sqlite3_initialize() ) return 0; #endif - sqlite3StrAccumInit(&acc, zBase, sizeof(zBase), SQLITE_MAX_LENGTH); - acc.useMalloc = 2; + sqlite3StrAccumInit(&acc, 0, zBase, sizeof(zBase), SQLITE_MAX_LENGTH); sqlite3VXPrintf(&acc, 0, zFormat, ap); z = sqlite3StrAccumFinish(&acc); return z; @@ -22021,7 +22316,7 @@ SQLITE_API char *sqlite3_vmprintf(const char *zFormat, va_list ap){ ** Print into memory obtained from sqlite3_malloc()(). Omit the internal ** %-conversion extensions. */ -SQLITE_API char *sqlite3_mprintf(const char *zFormat, ...){ +SQLITE_API char *SQLITE_CDECL sqlite3_mprintf(const char *zFormat, ...){ va_list ap; char *z; #ifndef SQLITE_OMIT_AUTOINIT @@ -22046,22 +22341,21 @@ SQLITE_API char *sqlite3_mprintf(const char *zFormat, ...){ ** ** sqlite3_vsnprintf() is the varargs version. */ -SQLITE_API char *sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_list ap){ +SQLITE_API char *SQLITE_STDCALL sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_list ap){ StrAccum acc; if( n<=0 ) return zBuf; #ifdef SQLITE_ENABLE_API_ARMOR if( zBuf==0 || zFormat==0 ) { (void)SQLITE_MISUSE_BKPT; - if( zBuf && n>0 ) zBuf[0] = 0; + if( zBuf ) zBuf[0] = 0; return zBuf; } #endif - sqlite3StrAccumInit(&acc, zBuf, n, 0); - acc.useMalloc = 0; + sqlite3StrAccumInit(&acc, 0, zBuf, n, 0); sqlite3VXPrintf(&acc, 0, zFormat, ap); return sqlite3StrAccumFinish(&acc); } -SQLITE_API char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){ +SQLITE_API char *SQLITE_CDECL sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){ char *z; va_list ap; va_start(ap,zFormat); @@ -22083,8 +22377,7 @@ static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){ StrAccum acc; /* String accumulator */ char zMsg[SQLITE_PRINT_BUF_SIZE*3]; /* Complete log message */ - sqlite3StrAccumInit(&acc, zMsg, sizeof(zMsg), 0); - acc.useMalloc = 0; + sqlite3StrAccumInit(&acc, 0, zMsg, sizeof(zMsg), 0); sqlite3VXPrintf(&acc, 0, zFormat, ap); sqlite3GlobalConfig.xLog(sqlite3GlobalConfig.pLogArg, iErrCode, sqlite3StrAccumFinish(&acc)); @@ -22093,7 +22386,7 @@ static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){ /* ** Format and write a message to the log if logging is enabled. */ -SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...){ +SQLITE_API void SQLITE_CDECL sqlite3_log(int iErrCode, const char *zFormat, ...){ va_list ap; /* Vararg list */ if( sqlite3GlobalConfig.xLog ){ va_start(ap, zFormat); @@ -22102,7 +22395,7 @@ SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...){ } } -#if defined(SQLITE_DEBUG) +#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) /* ** A version of printf() that understands %lld. Used for debugging. ** The printf() built into some versions of windows does not understand %lld @@ -22112,8 +22405,7 @@ SQLITE_PRIVATE void sqlite3DebugPrintf(const char *zFormat, ...){ va_list ap; StrAccum acc; char zBuf[500]; - sqlite3StrAccumInit(&acc, zBuf, sizeof(zBuf), 0); - acc.useMalloc = 0; + sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0); va_start(ap,zFormat); sqlite3VXPrintf(&acc, 0, zFormat, ap); va_end(ap); @@ -22140,7 +22432,7 @@ SQLITE_PRIVATE void sqlite3DebugPrintf(const char *zFormat, ...){ ** is not the last item in the tree. */ SQLITE_PRIVATE TreeView *sqlite3TreeViewPush(TreeView *p, u8 moreToFollow){ if( p==0 ){ - p = sqlite3_malloc( sizeof(*p) ); + p = sqlite3_malloc64( sizeof(*p) ); if( p==0 ) return 0; memset(p, 0, sizeof(*p)); }else{ @@ -22163,8 +22455,7 @@ SQLITE_PRIVATE void sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){ int i; StrAccum acc; char zBuf[500]; - sqlite3StrAccumInit(&acc, zBuf, sizeof(zBuf), 0); - acc.useMalloc = 0; + sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0); if( p ){ for(i=0; iiLevel && ibLine)-1; i++){ sqlite3StrAccumAppend(&acc, p->bLine[i] ? "| " : " ", 4); @@ -22229,7 +22520,7 @@ static SQLITE_WSD struct sqlite3PrngType { /* ** Return N random bytes. */ -SQLITE_API void sqlite3_randomness(int N, void *pBuf){ +SQLITE_API void SQLITE_STDCALL sqlite3_randomness(int N, void *pBuf){ unsigned char t; unsigned char *zBuf = pBuf; @@ -22435,7 +22726,7 @@ SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){ /********************************* Win32 Threads ****************************/ -#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_THREADSAFE>0 +#if SQLITE_OS_WIN_THREADS #define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */ #include @@ -22528,7 +22819,7 @@ SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){ return (rc==WAIT_OBJECT_0) ? SQLITE_OK : SQLITE_ERROR; } -#endif /* SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT */ +#endif /* SQLITE_OS_WIN_THREADS */ /******************************** End Win32 Threads *************************/ @@ -23381,7 +23672,7 @@ SQLITE_PRIVATE int sqlite3Dequote(char *z){ ** case-independent fashion, using the same definition of "case ** independence" that SQLite uses internally when comparing identifiers. */ -SQLITE_API int sqlite3_stricmp(const char *zLeft, const char *zRight){ +SQLITE_API int SQLITE_STDCALL sqlite3_stricmp(const char *zLeft, const char *zRight){ register unsigned char *a, *b; if( zLeft==0 ){ return zRight ? -1 : 0; @@ -23393,7 +23684,7 @@ SQLITE_API int sqlite3_stricmp(const char *zLeft, const char *zRight){ while( *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } return UpperToLower[*a] - UpperToLower[*b]; } -SQLITE_API int sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){ +SQLITE_API int SQLITE_STDCALL sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){ register unsigned char *a, *b; if( zLeft==0 ){ return zRight ? -1 : 0; @@ -23787,6 +24078,7 @@ SQLITE_PRIVATE int sqlite3GetInt32(const char *zNum, int *pValue){ } } #endif + while( zNum[0]=='0' ) zNum++; for(i=0; i<11 && (c = zNum[i] - '0')>=0 && c<=9; i++){ v = v*10 + c; } @@ -24924,23 +25216,25 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ /* 136 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"), /* 137 */ "IfPos" OpHelp("if r[P1]>0 goto P2"), /* 138 */ "IfNeg" OpHelp("r[P1]+=P3, if r[P1]<0 goto P2"), - /* 139 */ "IfZero" OpHelp("r[P1]+=P3, if r[P1]==0 goto P2"), - /* 140 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), - /* 141 */ "IncrVacuum" OpHelp(""), - /* 142 */ "Expire" OpHelp(""), - /* 143 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), - /* 144 */ "VBegin" OpHelp(""), - /* 145 */ "VCreate" OpHelp(""), - /* 146 */ "VDestroy" OpHelp(""), - /* 147 */ "VOpen" OpHelp(""), - /* 148 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), - /* 149 */ "VNext" OpHelp(""), - /* 150 */ "VRename" OpHelp(""), - /* 151 */ "Pagecount" OpHelp(""), - /* 152 */ "MaxPgcnt" OpHelp(""), - /* 153 */ "Init" OpHelp("Start at P2"), - /* 154 */ "Noop" OpHelp(""), - /* 155 */ "Explain" OpHelp(""), + /* 139 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]+=P3, goto P2"), + /* 140 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"), + /* 141 */ "JumpZeroIncr" OpHelp("if (r[P1]++)==0 ) goto P2"), + /* 142 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), + /* 143 */ "IncrVacuum" OpHelp(""), + /* 144 */ "Expire" OpHelp(""), + /* 145 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), + /* 146 */ "VBegin" OpHelp(""), + /* 147 */ "VCreate" OpHelp(""), + /* 148 */ "VDestroy" OpHelp(""), + /* 149 */ "VOpen" OpHelp(""), + /* 150 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), + /* 151 */ "VNext" OpHelp(""), + /* 152 */ "VRename" OpHelp(""), + /* 153 */ "Pagecount" OpHelp(""), + /* 154 */ "MaxPgcnt" OpHelp(""), + /* 155 */ "Init" OpHelp("Start at P2"), + /* 156 */ "Noop" OpHelp(""), + /* 157 */ "Explain" OpHelp(""), }; return azName[i]; } @@ -25020,18 +25314,6 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ # endif #endif -/* -** Define the OS_VXWORKS pre-processor macro to 1 if building on -** vxworks, or 0 otherwise. -*/ -#ifndef OS_VXWORKS -# if defined(__RTP__) || defined(_WRS_KERNEL) -# define OS_VXWORKS 1 -# else -# define OS_VXWORKS 0 -# endif -#endif - /* ** standard include files. */ @@ -25046,18 +25328,30 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ # include #endif -#if SQLITE_ENABLE_LOCKING_STYLE || OS_VXWORKS +#if SQLITE_ENABLE_LOCKING_STYLE # include -# if OS_VXWORKS -# include -# include -# else -# include -# include -# endif +# include +# include #endif /* SQLITE_ENABLE_LOCKING_STYLE */ -#if defined(__APPLE__) || (SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS) +#if defined(__APPLE__) && ((__MAC_OS_X_VERSION_MIN_REQUIRED > 1050) || \ + (__IPHONE_OS_VERSION_MIN_REQUIRED > 2000)) +# if (!defined(TARGET_OS_EMBEDDED) || (TARGET_OS_EMBEDDED==0)) \ + && (!defined(TARGET_IPHONE_SIMULATOR) || (TARGET_IPHONE_SIMULATOR==0)) +# define HAVE_GETHOSTUUID 1 +# else +# warning "gethostuuid() is disabled." +# endif +#endif + + +#if OS_VXWORKS +/* # include */ +# include +# include +#endif /* OS_VXWORKS */ + +#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE # include #endif @@ -25098,6 +25392,10 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ */ #define MAX_PATHNAME 512 +/* Always cast the getpid() return type for compatibility with +** kernel modules in VxWorks. */ +#define osGetpid(X) (pid_t)getpid() + /* ** Only set the lastErrno if the error code is a real error and not ** a normal expected return code of SQLITE_BUSY or SQLITE_OK @@ -25186,7 +25484,7 @@ struct unixFile { ** method was called. If xOpen() is called from a different process id, ** indicating that a fork() has occurred, the PRNG will be reset. */ -static int randomnessPid = 0; +static pid_t randomnessPid = 0; /* ** Allowed values for the unixFile.ctrlFlags bitmask: @@ -25203,7 +25501,8 @@ static int randomnessPid = 0; #define UNIXFILE_DELETE 0x20 /* Delete on close */ #define UNIXFILE_URI 0x40 /* Filename might have query parameters */ #define UNIXFILE_NOLOCK 0x80 /* Do no file locking */ -#define UNIXFILE_WARNED 0x0100 /* verifyDbFile() warnings have been issued */ +#define UNIXFILE_WARNED 0x0100 /* verifyDbFile() warnings issued */ +#define UNIXFILE_BLOCK 0x0200 /* Next SHM lock might block */ /* ** Include code that is common to all os_*.c files @@ -25241,16 +25540,6 @@ static int randomnessPid = 0; # error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." #endif -#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) -# ifndef SQLITE_DEBUG_OS_TRACE -# define SQLITE_DEBUG_OS_TRACE 0 -# endif - int sqlite3OSTrace = SQLITE_DEBUG_OS_TRACE; -# define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X -#else -# define OSTRACE(X) -#endif - /* ** Macros for performance tracing. Normally turned off. Only works ** on i486 hardware. @@ -25542,7 +25831,7 @@ static struct unix_syscall { { "read", (sqlite3_syscall_ptr)read, 0 }, #define osRead ((ssize_t(*)(int,void*,size_t))aSyscall[8].pCurrent) -#if defined(USE_PREAD) || (SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS) +#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE { "pread", (sqlite3_syscall_ptr)pread, 0 }, #else { "pread", (sqlite3_syscall_ptr)0, 0 }, @@ -25559,7 +25848,7 @@ static struct unix_syscall { { "write", (sqlite3_syscall_ptr)write, 0 }, #define osWrite ((ssize_t(*)(int,const void*,size_t))aSyscall[11].pCurrent) -#if defined(USE_PREAD) || (SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS) +#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE { "pwrite", (sqlite3_syscall_ptr)pwrite, 0 }, #else { "pwrite", (sqlite3_syscall_ptr)0, 0 }, @@ -25793,7 +26082,7 @@ static int unixMutexHeld(void) { #endif -#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) +#ifdef SQLITE_HAVE_OS_TRACE /* ** Helper function for printing out trace information from debugging ** binaries. This returns the string representation of the supplied @@ -25874,9 +26163,9 @@ static int lockTrace(int fd, int op, struct flock *p){ /* ** Retry ftruncate() calls that fail due to EINTR ** -** All calls to ftruncate() within this file should be made through this wrapper. -** On the Android platform, bypassing the logic below could lead to a corrupt -** database. +** All calls to ftruncate() within this file should be made through +** this wrapper. On the Android platform, bypassing the logic below +** could lead to a corrupt database. */ static int robust_ftruncate(int h, sqlite3_int64 sz){ int rc; @@ -26056,7 +26345,7 @@ static struct vxworksFileId *vxworksFindFileId(const char *zAbsoluteName){ assert( zAbsoluteName[0]=='/' ); n = (int)strlen(zAbsoluteName); - pNew = sqlite3_malloc( sizeof(*pNew) + (n+1) ); + pNew = sqlite3_malloc64( sizeof(*pNew) + (n+1) ); if( pNew==0 ) return 0; pNew->zCanonicalName = (char*)&pNew[1]; memcpy(pNew->zCanonicalName, zAbsoluteName, n+1); @@ -26335,6 +26624,14 @@ static void robust_close(unixFile *pFile, int h, int lineno){ } } +/* +** Set the pFile->lastErrno. Do this in a subroutine as that provides +** a convenient place to set a breakpoint. +*/ +static void storeLastErrno(unixFile *pFile, int error){ + pFile->lastErrno = error; +} + /* ** Close all file descriptors accumuated in the unixInodeInfo->pUnused list. */ @@ -26408,7 +26705,7 @@ static int findInodeInfo( fd = pFile->h; rc = osFstat(fd, &statbuf); if( rc!=0 ){ - pFile->lastErrno = errno; + storeLastErrno(pFile, errno); #ifdef EOVERFLOW if( pFile->lastErrno==EOVERFLOW ) return SQLITE_NOLFS; #endif @@ -26429,12 +26726,12 @@ static int findInodeInfo( if( statbuf.st_size==0 && (pFile->fsFlags & SQLITE_FSFLAGS_IS_MSDOS)!=0 ){ do{ rc = osWrite(fd, "S", 1); }while( rc<0 && errno==EINTR ); if( rc!=1 ){ - pFile->lastErrno = errno; + storeLastErrno(pFile, errno); return SQLITE_IOERR; } rc = osFstat(fd, &statbuf); if( rc!=0 ){ - pFile->lastErrno = errno; + storeLastErrno(pFile, errno); return SQLITE_IOERR; } } @@ -26452,7 +26749,7 @@ static int findInodeInfo( pInode = pInode->pNext; } if( pInode==0 ){ - pInode = sqlite3_malloc( sizeof(*pInode) ); + pInode = sqlite3_malloc64( sizeof(*pInode) ); if( pInode==0 ){ return SQLITE_NOMEM; } @@ -26557,7 +26854,7 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){ lock.l_type = F_WRLCK; if( osFcntl(pFile->h, F_GETLK, &lock) ){ rc = SQLITE_IOERR_CHECKRESERVEDLOCK; - pFile->lastErrno = errno; + storeLastErrno(pFile, errno); } else if( lock.l_type!=F_UNLCK ){ reserved = 1; } @@ -26690,7 +26987,8 @@ static int unixLock(sqlite3_file *id, int eFileLock){ assert( pFile ); OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (unix)\n", pFile->h, azFileLock(eFileLock), azFileLock(pFile->eFileLock), - azFileLock(pFile->pInode->eFileLock), pFile->pInode->nShared , getpid())); + azFileLock(pFile->pInode->eFileLock), pFile->pInode->nShared, + osGetpid(0))); /* If there is already a lock of this type or more restrictive on the ** unixFile, do nothing. Don't use the end_lock: exit path, as @@ -26757,7 +27055,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( rc!=SQLITE_BUSY ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); } goto end_lock; } @@ -26792,7 +27090,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ if( rc ){ if( rc!=SQLITE_BUSY ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); } goto end_lock; }else{ @@ -26825,7 +27123,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( rc!=SQLITE_BUSY ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); } } } @@ -26898,7 +27196,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ assert( pFile ); OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (unix)\n", pFile->h, eFileLock, pFile->eFileLock, pFile->pInode->eFileLock, pFile->pInode->nShared, - getpid())); + osGetpid(0))); assert( eFileLock<=SHARED_LOCK ); if( pFile->eFileLock<=eFileLock ){ @@ -26932,7 +27230,6 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ ** 4: [RRRR.] */ if( eFileLock==SHARED_LOCK ){ - #if !defined(__APPLE__) || !SQLITE_ENABLE_LOCKING_STYLE (void)handleNFSUnlock; assert( handleNFSUnlock==0 ); @@ -26950,7 +27247,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ tErrno = errno; rc = SQLITE_IOERR_UNLOCK; if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); } goto end_unlock; } @@ -26962,7 +27259,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK); if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); } goto end_unlock; } @@ -26974,7 +27271,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ tErrno = errno; rc = SQLITE_IOERR_UNLOCK; if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); } goto end_unlock; } @@ -26993,7 +27290,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ ** SQLITE_BUSY would confuse the upper layer (in practice it causes ** an assert to fail). */ rc = SQLITE_IOERR_RDLOCK; - pFile->lastErrno = errno; + storeLastErrno(pFile, errno); goto end_unlock; } } @@ -27006,7 +27303,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ pInode->eFileLock = SHARED_LOCK; }else{ rc = SQLITE_IOERR_UNLOCK; - pFile->lastErrno = errno; + storeLastErrno(pFile, errno); goto end_unlock; } } @@ -27024,7 +27321,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ pInode->eFileLock = NO_LOCK; }else{ rc = SQLITE_IOERR_UNLOCK; - pFile->lastErrno = errno; + storeLastErrno(pFile, errno); pInode->eFileLock = NO_LOCK; pFile->eFileLock = NO_LOCK; } @@ -27299,7 +27596,7 @@ static int dotlockLock(sqlite3_file *id, int eFileLock) { } else { rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); } } return rc; @@ -27326,7 +27623,7 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) { assert( pFile ); OSTRACE(("UNLOCK %d %d was %d pid=%d (dotlock)\n", pFile->h, eFileLock, - pFile->eFileLock, getpid())); + pFile->eFileLock, osGetpid(0))); assert( eFileLock<=SHARED_LOCK ); /* no-op if possible */ @@ -27353,7 +27650,7 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) { rc = SQLITE_IOERR_UNLOCK; } if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); } return rc; } @@ -27389,10 +27686,9 @@ static int dotlockClose(sqlite3_file *id) { ** still works when you do this, but concurrency is reduced since ** only a single process can be reading the database at a time. ** -** Omit this section if SQLITE_ENABLE_LOCKING_STYLE is turned off or if -** compiling for VXWORKS. +** Omit this section if SQLITE_ENABLE_LOCKING_STYLE is turned off */ -#if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS +#if SQLITE_ENABLE_LOCKING_STYLE /* ** Retry flock() calls that fail with EINTR @@ -27440,7 +27736,7 @@ static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){ /* unlock failed with an error */ lrc = SQLITE_IOERR_UNLOCK; if( IS_LOCK_ERROR(lrc) ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); rc = lrc; } } @@ -27450,7 +27746,7 @@ static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){ /* someone else might have it reserved */ lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( IS_LOCK_ERROR(lrc) ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); rc = lrc; } } @@ -27516,7 +27812,7 @@ static int flockLock(sqlite3_file *id, int eFileLock) { /* didn't get, must be busy */ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); } } else { /* got it, set the type and return ok */ @@ -27545,7 +27841,7 @@ static int flockUnlock(sqlite3_file *id, int eFileLock) { assert( pFile ); OSTRACE(("UNLOCK %d %d was %d pid=%d (flock)\n", pFile->h, eFileLock, - pFile->eFileLock, getpid())); + pFile->eFileLock, osGetpid(0))); assert( eFileLock<=SHARED_LOCK ); /* no-op if possible */ @@ -27606,7 +27902,7 @@ static int flockClose(sqlite3_file *id) { ** to a non-zero value otherwise *pResOut is set to zero. The return value ** is set to SQLITE_OK unless an I/O error occurs during lock checking. */ -static int semCheckReservedLock(sqlite3_file *id, int *pResOut) { +static int semXCheckReservedLock(sqlite3_file *id, int *pResOut) { int rc = SQLITE_OK; int reserved = 0; unixFile *pFile = (unixFile*)id; @@ -27628,7 +27924,7 @@ static int semCheckReservedLock(sqlite3_file *id, int *pResOut) { int tErrno = errno; if( EAGAIN != tErrno ){ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_CHECKRESERVEDLOCK); - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); } else { /* someone else has the lock when we are in NO_LOCK */ reserved = (pFile->eFileLock < SHARED_LOCK); @@ -27673,7 +27969,7 @@ static int semCheckReservedLock(sqlite3_file *id, int *pResOut) { ** This routine will only increase a lock. Use the sqlite3OsUnlock() ** routine to lower a locking level. */ -static int semLock(sqlite3_file *id, int eFileLock) { +static int semXLock(sqlite3_file *id, int eFileLock) { unixFile *pFile = (unixFile*)id; sem_t *pSem = pFile->pInode->pSem; int rc = SQLITE_OK; @@ -27706,14 +28002,14 @@ static int semLock(sqlite3_file *id, int eFileLock) { ** If the locking level of the file descriptor is already at or below ** the requested locking level, this routine is a no-op. */ -static int semUnlock(sqlite3_file *id, int eFileLock) { +static int semXUnlock(sqlite3_file *id, int eFileLock) { unixFile *pFile = (unixFile*)id; sem_t *pSem = pFile->pInode->pSem; assert( pFile ); assert( pSem ); OSTRACE(("UNLOCK %d %d was %d pid=%d (sem)\n", pFile->h, eFileLock, - pFile->eFileLock, getpid())); + pFile->eFileLock, osGetpid(0))); assert( eFileLock<=SHARED_LOCK ); /* no-op if possible */ @@ -27732,7 +28028,7 @@ static int semUnlock(sqlite3_file *id, int eFileLock) { int rc, tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); } return rc; } @@ -27743,10 +28039,10 @@ static int semUnlock(sqlite3_file *id, int eFileLock) { /* ** Close a file. */ -static int semClose(sqlite3_file *id) { +static int semXClose(sqlite3_file *id) { if( id ){ unixFile *pFile = (unixFile*)id; - semUnlock(id, NO_LOCK); + semXUnlock(id, NO_LOCK); assert( pFile ); unixEnterMutex(); releaseInodeInfo(pFile); @@ -27834,7 +28130,7 @@ static int afpSetLock( setLockFlag ? SQLITE_IOERR_LOCK : SQLITE_IOERR_UNLOCK); #endif /* SQLITE_IGNORE_AFP_LOCK_ERRORS */ if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); } return rc; } else { @@ -27927,7 +28223,7 @@ static int afpLock(sqlite3_file *id, int eFileLock){ assert( pFile ); OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (afp)\n", pFile->h, azFileLock(eFileLock), azFileLock(pFile->eFileLock), - azFileLock(pInode->eFileLock), pInode->nShared , getpid())); + azFileLock(pInode->eFileLock), pInode->nShared , osGetpid(0))); /* If there is already a lock of this type or more restrictive on the ** unixFile, do nothing. Don't use the afp_end_lock: exit path, as @@ -28017,7 +28313,7 @@ static int afpLock(sqlite3_file *id, int eFileLock){ lrc2 = afpSetLock(context->dbPath, pFile, PENDING_BYTE, 1, 0); if( IS_LOCK_ERROR(lrc1) ) { - pFile->lastErrno = lrc1Errno; + storeLastErrno(pFile, lrc1Errno); rc = lrc1; goto afp_end_lock; } else if( IS_LOCK_ERROR(lrc2) ){ @@ -28113,7 +28409,7 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) { assert( pFile ); OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (afp)\n", pFile->h, eFileLock, pFile->eFileLock, pFile->pInode->eFileLock, pFile->pInode->nShared, - getpid())); + osGetpid(0))); assert( eFileLock<=SHARED_LOCK ); if( pFile->eFileLock<=eFileLock ){ @@ -28304,9 +28600,9 @@ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ SimulateIOError( newOffset-- ); if( newOffset!=offset ){ if( newOffset == -1 ){ - ((unixFile*)id)->lastErrno = errno; + storeLastErrno((unixFile*)id, errno); }else{ - ((unixFile*)id)->lastErrno = 0; + storeLastErrno((unixFile*)id, 0); } return -1; } @@ -28316,7 +28612,7 @@ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ if( got<0 ){ if( errno==EINTR ){ got = 1; continue; } prior = 0; - ((unixFile*)id)->lastErrno = errno; + storeLastErrno((unixFile*)id, errno); break; }else if( got>0 ){ cnt -= got; @@ -28381,7 +28677,7 @@ static int unixRead( /* lastErrno set by seekAndRead */ return SQLITE_IOERR_READ; }else{ - pFile->lastErrno = 0; /* not a system error */ + storeLastErrno(pFile, 0); /* not a system error */ /* Unread parts of the buffer must be zero-filled */ memset(&((char*)pBuf)[got], 0, amt-got); return SQLITE_IOERR_SHORT_READ; @@ -28410,9 +28706,9 @@ static int seekAndWriteFd( TIMER_START; #if defined(USE_PREAD) - do{ rc = osPwrite(fd, pBuf, nBuf, iOff); }while( rc<0 && errno==EINTR ); + do{ rc = (int)osPwrite(fd, pBuf, nBuf, iOff); }while( rc<0 && errno==EINTR ); #elif defined(USE_PREAD64) - do{ rc = osPwrite64(fd, pBuf, nBuf, iOff);}while( rc<0 && errno==EINTR); + do{ rc = (int)osPwrite64(fd, pBuf, nBuf, iOff);}while( rc<0 && errno==EINTR); #else do{ i64 iSeek = lseek(fd, iOff, SEEK_SET); @@ -28522,7 +28818,7 @@ static int unixWrite( /* lastErrno set by seekAndWrite */ return SQLITE_IOERR_WRITE; }else{ - pFile->lastErrno = 0; /* not a system error */ + storeLastErrno(pFile, 0); /* not a system error */ return SQLITE_FULL; } } @@ -28731,7 +29027,7 @@ static int unixSync(sqlite3_file *id, int flags){ rc = full_fsync(pFile->h, isFullsync, isDataOnly); SimulateIOError( rc=1 ); if( rc ){ - pFile->lastErrno = errno; + storeLastErrno(pFile, errno); return unixLogError(SQLITE_IOERR_FSYNC, "full_fsync", pFile->zPath); } @@ -28775,7 +29071,7 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){ rc = robust_ftruncate(pFile->h, nByte); if( rc ){ - pFile->lastErrno = errno; + storeLastErrno(pFile, errno); return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); }else{ #ifdef SQLITE_DEBUG @@ -28815,7 +29111,7 @@ static int unixFileSize(sqlite3_file *id, i64 *pSize){ rc = osFstat(((unixFile*)id)->h, &buf); SimulateIOError( rc=1 ); if( rc!=0 ){ - ((unixFile*)id)->lastErrno = errno; + storeLastErrno((unixFile*)id, errno); return SQLITE_IOERR_FSTAT; } *pSize = buf.st_size; @@ -28851,7 +29147,9 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ i64 nSize; /* Required file size */ struct stat buf; /* Used to hold return values of fstat() */ - if( osFstat(pFile->h, &buf) ) return SQLITE_IOERR_FSTAT; + if( osFstat(pFile->h, &buf) ){ + return SQLITE_IOERR_FSTAT; + } nSize = ((nByte+pFile->szChunk-1) / pFile->szChunk) * pFile->szChunk; if( nSize>(i64)buf.st_size ){ @@ -28898,7 +29196,7 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ int rc; if( pFile->szChunk<=0 ){ if( robust_ftruncate(pFile->h, nByte) ){ - pFile->lastErrno = errno; + storeLastErrno(pFile, errno); return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); } } @@ -28936,11 +29234,15 @@ static int unixGetTempname(int nBuf, char *zBuf); static int unixFileControl(sqlite3_file *id, int op, void *pArg){ unixFile *pFile = (unixFile*)id; switch( op ){ + case SQLITE_FCNTL_WAL_BLOCK: { + /* pFile->ctrlFlags |= UNIXFILE_BLOCK; // Deferred feature */ + return SQLITE_OK; + } case SQLITE_FCNTL_LOCKSTATE: { *(int*)pArg = pFile->eFileLock; return SQLITE_OK; } - case SQLITE_LAST_ERRNO: { + case SQLITE_FCNTL_LAST_ERRNO: { *(int*)pArg = pFile->lastErrno; return SQLITE_OK; } @@ -28968,7 +29270,7 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ return SQLITE_OK; } case SQLITE_FCNTL_TEMPFILENAME: { - char *zTFile = sqlite3_malloc( pFile->pVfs->mxPathname ); + char *zTFile = sqlite3_malloc64( pFile->pVfs->mxPathname ); if( zTFile ){ unixGetTempname(pFile->pVfs->mxPathname, zTFile); *(char**)pArg = zTFile; @@ -29009,8 +29311,8 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ } #endif #if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) - case SQLITE_SET_LOCKPROXYFILE: - case SQLITE_GET_LOCKPROXYFILE: { + case SQLITE_FCNTL_SET_LOCKPROXYFILE: + case SQLITE_FCNTL_GET_LOCKPROXYFILE: { return proxyFileControl(id,op,pArg); } #endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */ @@ -29150,7 +29452,9 @@ static int unixDeviceCharacteristics(sqlite3_file *id){ ** Instead, it should be called via macro osGetpagesize(). */ static int unixGetpagesize(void){ -#if defined(_BSD_SOURCE) +#if OS_VXWORKS + return 1024; +#elif defined(_BSD_SOURCE) return getpagesize(); #else return (int)sysconf(_SC_PAGESIZE); @@ -29243,15 +29547,17 @@ struct unixShm { ** otherwise. */ static int unixShmSystemLock( - unixShmNode *pShmNode, /* Apply locks to this open shared-memory segment */ + unixFile *pFile, /* Open connection to the WAL file */ int lockType, /* F_UNLCK, F_RDLCK, or F_WRLCK */ int ofst, /* First byte of the locking range */ int n /* Number of bytes to lock */ ){ - struct flock f; /* The posix advisory locking structure */ - int rc = SQLITE_OK; /* Result code form fcntl() */ + unixShmNode *pShmNode; /* Apply locks to this open shared-memory segment */ + struct flock f; /* The posix advisory locking structure */ + int rc = SQLITE_OK; /* Result code form fcntl() */ /* Access to the unixShmNode object is serialized by the caller */ + pShmNode = pFile->pInode->pShmNode; assert( sqlite3_mutex_held(pShmNode->mutex) || pShmNode->nRef==0 ); /* Shared locks never span more than one byte */ @@ -29261,6 +29567,7 @@ static int unixShmSystemLock( assert( n>=1 && nh>=0 ){ + int lkType; /* Initialize the locking parameters */ memset(&f, 0, sizeof(f)); f.l_type = lockType; @@ -29268,8 +29575,10 @@ static int unixShmSystemLock( f.l_start = ofst; f.l_len = n; - rc = osFcntl(pShmNode->h, F_SETLK, &f); + lkType = (pFile->ctrlFlags & UNIXFILE_BLOCK)!=0 ? F_SETLKW : F_SETLK; + rc = osFcntl(pShmNode->h, lkType, &f); rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY; + pFile->ctrlFlags &= ~UNIXFILE_BLOCK; } /* Update the global lock state and do debug tracing */ @@ -29402,7 +29711,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ int nShmFilename; /* Size of the SHM filename in bytes */ /* Allocate space for the new unixShm object. */ - p = sqlite3_malloc( sizeof(*p) ); + p = sqlite3_malloc64( sizeof(*p) ); if( p==0 ) return SQLITE_NOMEM; memset(p, 0, sizeof(*p)); assert( pDbFd->pShm==0 ); @@ -29415,6 +29724,9 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ pShmNode = pInode->pShmNode; if( pShmNode==0 ){ struct stat sStat; /* fstat() info for database file */ +#ifndef SQLITE_SHM_DIRECTORY + const char *zBasePath = pDbFd->zPath; +#endif /* Call fstat() to figure out the permissions on the database file. If ** a new *-shm file is created, an attempt will be made to create it @@ -29428,9 +29740,9 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ #ifdef SQLITE_SHM_DIRECTORY nShmFilename = sizeof(SQLITE_SHM_DIRECTORY) + 31; #else - nShmFilename = 6 + (int)strlen(pDbFd->zPath); + nShmFilename = 6 + (int)strlen(zBasePath); #endif - pShmNode = sqlite3_malloc( sizeof(*pShmNode) + nShmFilename ); + pShmNode = sqlite3_malloc64( sizeof(*pShmNode) + nShmFilename ); if( pShmNode==0 ){ rc = SQLITE_NOMEM; goto shm_open_err; @@ -29442,7 +29754,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ SQLITE_SHM_DIRECTORY "/sqlite-shm-%x-%x", (u32)sStat.st_ino, (u32)sStat.st_dev); #else - sqlite3_snprintf(nShmFilename, zShmFilename, "%s-shm", pDbFd->zPath); + sqlite3_snprintf(nShmFilename, zShmFilename, "%s-shm", zBasePath); sqlite3FileSuffix3(pDbFd->zPath, zShmFilename); #endif pShmNode->h = -1; @@ -29476,13 +29788,13 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ ** If not, truncate the file to zero length. */ rc = SQLITE_OK; - if( unixShmSystemLock(pShmNode, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){ + if( unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){ if( robust_ftruncate(pShmNode->h, 0) ){ rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename); } } if( rc==SQLITE_OK ){ - rc = unixShmSystemLock(pShmNode, F_RDLCK, UNIX_SHM_DMS, 1); + rc = unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, 1); } if( rc ) goto shm_open_err; } @@ -29640,7 +29952,7 @@ static int unixShmMap( goto shmpage_out; } }else{ - pMem = sqlite3_malloc(szRegion); + pMem = sqlite3_malloc64(szRegion); if( pMem==0 ){ rc = SQLITE_NOMEM; goto shmpage_out; @@ -29714,7 +30026,7 @@ static int unixShmLock( /* Unlock the system-level locks */ if( (mask & allMask)==0 ){ - rc = unixShmSystemLock(pShmNode, F_UNLCK, ofst+UNIX_SHM_BASE, n); + rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n); }else{ rc = SQLITE_OK; } @@ -29742,7 +30054,7 @@ static int unixShmLock( /* Get shared locks at the system level, if necessary */ if( rc==SQLITE_OK ){ if( (allShared & mask)==0 ){ - rc = unixShmSystemLock(pShmNode, F_RDLCK, ofst+UNIX_SHM_BASE, n); + rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n); }else{ rc = SQLITE_OK; } @@ -29767,7 +30079,7 @@ static int unixShmLock( ** also mark the local connection as being locked. */ if( rc==SQLITE_OK ){ - rc = unixShmSystemLock(pShmNode, F_WRLCK, ofst+UNIX_SHM_BASE, n); + rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n); if( rc==SQLITE_OK ){ assert( (p->sharedMask & mask)==0 ); p->exclMask |= mask; @@ -29776,7 +30088,7 @@ static int unixShmLock( } sqlite3_mutex_leave(pShmNode->mutex); OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n", - p->id, getpid(), p->sharedMask, p->exclMask)); + p->id, osGetpid(0), p->sharedMask, p->exclMask)); return rc; } @@ -29835,7 +30147,9 @@ static int unixShmUnmap( assert( pShmNode->nRef>0 ); pShmNode->nRef--; if( pShmNode->nRef==0 ){ - if( deleteFlag && pShmNode->h>=0 ) osUnlink(pShmNode->zFilename); + if( deleteFlag && pShmNode->h>=0 ){ + osUnlink(pShmNode->zFilename); + } unixShmPurge(pDbFd); } unixLeaveMutex(); @@ -30112,7 +30426,7 @@ static int unixUnfetch(sqlite3_file *fd, i64 iOff, void *p){ ** * An I/O method finder function called FINDER that returns a pointer ** to the METHOD object in the previous bullet. */ -#define IOMETHODS(FINDER, METHOD, VERSION, CLOSE, LOCK, UNLOCK, CKLOCK, SHMMAP) \ +#define IOMETHODS(FINDER,METHOD,VERSION,CLOSE,LOCK,UNLOCK,CKLOCK,SHMMAP) \ static const sqlite3_io_methods METHOD = { \ VERSION, /* iVersion */ \ CLOSE, /* xClose */ \ @@ -30177,7 +30491,7 @@ IOMETHODS( 0 /* xShmMap method */ ) -#if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS +#if SQLITE_ENABLE_LOCKING_STYLE IOMETHODS( flockIoFinder, /* Finder function name */ flockIoMethods, /* sqlite3_io_methods object name */ @@ -30195,10 +30509,10 @@ IOMETHODS( semIoFinder, /* Finder function name */ semIoMethods, /* sqlite3_io_methods object name */ 1, /* shared memory is disabled */ - semClose, /* xClose method */ - semLock, /* xLock method */ - semUnlock, /* xUnlock method */ - semCheckReservedLock, /* xCheckReservedLock method */ + semXClose, /* xClose method */ + semXLock, /* xLock method */ + semXUnlock, /* xUnlock method */ + semXCheckReservedLock, /* xCheckReservedLock method */ 0 /* xShmMap method */ ) #endif @@ -30322,15 +30636,13 @@ static const sqlite3_io_methods #endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ -#if OS_VXWORKS && SQLITE_ENABLE_LOCKING_STYLE -/* -** This "finder" function attempts to determine the best locking strategy -** for the database file "filePath". It then returns the sqlite3_io_methods -** object that implements that strategy. -** -** This is for VXWorks only. +#if OS_VXWORKS +/* +** This "finder" function for VxWorks checks to see if posix advisory +** locking works. If it does, then that is what is used. If it does not +** work, then fallback to named semaphore locking. */ -static const sqlite3_io_methods *autolockIoFinderImpl( +static const sqlite3_io_methods *vxworksIoFinderImpl( const char *filePath, /* name of the database file */ unixFile *pNew /* the open file object */ ){ @@ -30356,9 +30668,9 @@ static const sqlite3_io_methods *autolockIoFinderImpl( } } static const sqlite3_io_methods - *(*const autolockIoFinder)(const char*,unixFile*) = autolockIoFinderImpl; + *(*const vxworksIoFinder)(const char*,unixFile*) = vxworksIoFinderImpl; -#endif /* OS_VXWORKS && SQLITE_ENABLE_LOCKING_STYLE */ +#endif /* OS_VXWORKS */ /* ** An abstract type for a pointer to an IO method finder function: @@ -30477,7 +30789,7 @@ static int fillInUnixFile( ** the afpLockingContext. */ afpLockingContext *pCtx; - pNew->lockingContext = pCtx = sqlite3_malloc( sizeof(*pCtx) ); + pNew->lockingContext = pCtx = sqlite3_malloc64( sizeof(*pCtx) ); if( pCtx==0 ){ rc = SQLITE_NOMEM; }else{ @@ -30507,7 +30819,7 @@ static int fillInUnixFile( int nFilename; assert( zFilename!=0 ); nFilename = (int)strlen(zFilename) + 6; - zLockFile = (char *)sqlite3_malloc(nFilename); + zLockFile = (char *)sqlite3_malloc64(nFilename); if( zLockFile==0 ){ rc = SQLITE_NOMEM; }else{ @@ -30540,7 +30852,7 @@ static int fillInUnixFile( } #endif - pNew->lastErrno = 0; + storeLastErrno(pNew, 0); #if OS_VXWORKS if( rc!=SQLITE_OK ){ if( h>=0 ) robust_close(pNew, h, __LINE__); @@ -30871,8 +31183,8 @@ static int unixOpen( ** the same instant might all reset the PRNG. But multiple resets ** are harmless. */ - if( randomnessPid!=getpid() ){ - randomnessPid = getpid(); + if( randomnessPid!=osGetpid(0) ){ + randomnessPid = osGetpid(0); sqlite3_randomness(0,0); } @@ -30884,7 +31196,7 @@ static int unixOpen( if( pUnused ){ fd = pUnused->fd; }else{ - pUnused = sqlite3_malloc(sizeof(*pUnused)); + pUnused = sqlite3_malloc64(sizeof(*pUnused)); if( !pUnused ){ return SQLITE_NOMEM; } @@ -30988,13 +31300,16 @@ static int unixOpen( #if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE if( fstatfs(fd, &fsInfo) == -1 ){ - ((unixFile*)pFile)->lastErrno = errno; + storeLastErrno(p, errno); robust_close(p, fd, __LINE__); return SQLITE_IOERR_ACCESS; } if (0 == strncmp("msdos", fsInfo.f_fstypename, 5)) { ((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_MSDOS; } + if (0 == strncmp("exfat", fsInfo.f_fstypename, 5)) { + ((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_MSDOS; + } #endif /* Set up appropriate ctrlFlags */ @@ -31017,19 +31332,6 @@ static int unixOpen( if( envforce!=NULL ){ useProxy = atoi(envforce)>0; }else{ - if( statfs(zPath, &fsInfo) == -1 ){ - /* In theory, the close(fd) call is sub-optimal. If the file opened - ** with fd is a database file, and there are other connections open - ** on that file that are currently holding advisory locks on it, - ** then the call to close() will cancel those locks. In practice, - ** we're assuming that statfs() doesn't fail very often. At least - ** not while other file descriptors opened by the same process on - ** the same file are working. */ - p->lastErrno = errno; - robust_close(p, fd, __LINE__); - rc = SQLITE_IOERR_ACCESS; - goto open_finished; - } useProxy = !(fsInfo.f_flags&MNT_LOCAL); } if( useProxy ){ @@ -31273,8 +31575,8 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){ ** tests repeatable. */ memset(zBuf, 0, nBuf); - randomnessPid = getpid(); -#if !defined(SQLITE_TEST) + randomnessPid = osGetpid(0); +#if !defined(SQLITE_TEST) && !defined(SQLITE_OMIT_RANDOMNESS) { int fd, got; fd = robust_open("/dev/urandom", O_RDONLY, 0); @@ -31455,9 +31757,10 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){ ** ** C APIs ** -** sqlite3_file_control(db, dbname, SQLITE_SET_LOCKPROXYFILE, +** sqlite3_file_control(db, dbname, SQLITE_FCNTL_SET_LOCKPROXYFILE, ** | ":auto:"); -** sqlite3_file_control(db, dbname, SQLITE_GET_LOCKPROXYFILE, &); +** sqlite3_file_control(db, dbname, SQLITE_FCNTL_GET_LOCKPROXYFILE, +** &); ** ** ** SQL pragmas @@ -31550,7 +31853,7 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){ ** setting the environment variable SQLITE_FORCE_PROXY_LOCKING to 1 will ** force proxy locking to be used for every database file opened, and 0 ** will force automatic proxy locking to be disabled for all database -** files (explicitly calling the SQLITE_SET_LOCKPROXYFILE pragma or +** files (explicitly calling the SQLITE_FCNTL_SET_LOCKPROXYFILE pragma or ** sqlite_file_control API is not affected by SQLITE_FORCE_PROXY_LOCKING). */ @@ -31571,6 +31874,7 @@ struct proxyLockingContext { char *lockProxyPath; /* Name of the proxy lock file */ char *dbPath; /* Name of the open file */ int conchHeld; /* 1 if the conch is held, -1 if lockless */ + int nFails; /* Number of conch taking failures */ void *oldLockingContext; /* Original lockingcontext to restore on close */ sqlite3_io_methods const *pOldMethod; /* Original I/O methods for close */ }; @@ -31592,7 +31896,7 @@ static int proxyGetLockPath(const char *dbPath, char *lPath, size_t maxLen){ { if( !confstr(_CS_DARWIN_USER_TEMP_DIR, lPath, maxLen) ){ OSTRACE(("GETLOCKPATH failed %s errno=%d pid=%d\n", - lPath, errno, getpid())); + lPath, errno, osGetpid(0))); return SQLITE_IOERR_LOCK; } len = strlcat(lPath, "sqliteplocks", maxLen); @@ -31614,7 +31918,7 @@ static int proxyGetLockPath(const char *dbPath, char *lPath, size_t maxLen){ } lPath[i+len]='\0'; strlcat(lPath, ":auto:", maxLen); - OSTRACE(("GETLOCKPATH proxy lock path=%s pid=%d\n", lPath, getpid())); + OSTRACE(("GETLOCKPATH proxy lock path=%s pid=%d\n", lPath, osGetpid(0))); return SQLITE_OK; } @@ -31641,7 +31945,7 @@ static int proxyCreateLockPath(const char *lockPath){ if( err!=EEXIST ) { OSTRACE(("CREATELOCKPATH FAILED creating %s, " "'%s' proxy lock path=%s pid=%d\n", - buf, strerror(err), lockPath, getpid())); + buf, strerror(err), lockPath, osGetpid(0))); return err; } } @@ -31650,7 +31954,7 @@ static int proxyCreateLockPath(const char *lockPath){ } buf[i] = lockPath[i]; } - OSTRACE(("CREATELOCKPATH proxy lock path=%s pid=%d\n", lockPath, getpid())); + OSTRACE(("CREATELOCKPATH proxy lock path=%s pid=%d\n", lockPath, osGetpid(0))); return 0; } @@ -31684,7 +31988,7 @@ static int proxyCreateUnixFile( if( pUnused ){ fd = pUnused->fd; }else{ - pUnused = sqlite3_malloc(sizeof(*pUnused)); + pUnused = sqlite3_malloc64(sizeof(*pUnused)); if( !pUnused ){ return SQLITE_NOMEM; } @@ -31717,7 +32021,7 @@ static int proxyCreateUnixFile( } } - pNew = (unixFile *)sqlite3_malloc(sizeof(*pNew)); + pNew = (unixFile *)sqlite3_malloc64(sizeof(*pNew)); if( pNew==NULL ){ rc = SQLITE_NOMEM; goto end_create_proxy; @@ -31750,8 +32054,10 @@ SQLITE_API int sqlite3_hostid_num = 0; #define PROXY_HOSTIDLEN 16 /* conch file host id length */ +#ifdef HAVE_GETHOSTUUID /* Not always defined in the headers as it ought to be */ extern int gethostuuid(uuid_t id, const struct timespec *wait); +#endif /* get the host ID via gethostuuid(), pHostID must point to PROXY_HOSTIDLEN ** bytes of writable memory. @@ -31759,10 +32065,9 @@ extern int gethostuuid(uuid_t id, const struct timespec *wait); static int proxyGetHostID(unsigned char *pHostID, int *pError){ assert(PROXY_HOSTIDLEN == sizeof(uuid_t)); memset(pHostID, 0, PROXY_HOSTIDLEN); -#if defined(__MAX_OS_X_VERSION_MIN_REQUIRED)\ - && __MAC_OS_X_VERSION_MIN_REQUIRED<1050 +#ifdef HAVE_GETHOSTUUID { - static const struct timespec timeout = {1, 0}; /* 1 sec timeout */ + struct timespec timeout = {1, 0}; /* 1 sec timeout */ if( gethostuuid(pHostID, &timeout) ){ int err = errno; if( pError ){ @@ -31877,7 +32182,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ */ struct stat buf; if( osFstat(conchFile->h, &buf) ){ - pFile->lastErrno = errno; + storeLastErrno(pFile, errno); return SQLITE_IOERR_LOCK; } @@ -31897,7 +32202,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ char tBuf[PROXY_MAXCONCHLEN]; int len = osPread(conchFile->h, tBuf, PROXY_MAXCONCHLEN, 0); if( len<0 ){ - pFile->lastErrno = errno; + storeLastErrno(pFile, errno); return SQLITE_IOERR_LOCK; } if( len>PROXY_PATHINDEX && tBuf[0]==(char)PROXY_CONCHVERSION){ @@ -31917,7 +32222,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ if( 0==proxyBreakConchLock(pFile, myHostID) ){ rc = SQLITE_OK; if( lockType==EXCLUSIVE_LOCK ){ - rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, SHARED_LOCK); + rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, SHARED_LOCK); } if( !rc ){ rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, lockType); @@ -31955,11 +32260,12 @@ static int proxyTakeConch(unixFile *pFile){ int forceNewLockPath = 0; OSTRACE(("TAKECONCH %d for %s pid=%d\n", conchFile->h, - (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"), getpid())); + (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"), + osGetpid(0))); rc = proxyGetHostID(myHostID, &pError); if( (rc&0xff)==SQLITE_IOERR ){ - pFile->lastErrno = pError; + storeLastErrno(pFile, pError); goto end_takeconch; } rc = proxyConchLock(pFile, myHostID, SHARED_LOCK); @@ -31970,7 +32276,7 @@ static int proxyTakeConch(unixFile *pFile){ readLen = seekAndRead((unixFile*)conchFile, 0, readBuf, PROXY_MAXCONCHLEN); if( readLen<0 ){ /* I/O error: lastErrno set by seekAndRead */ - pFile->lastErrno = conchFile->lastErrno; + storeLastErrno(pFile, conchFile->lastErrno); rc = SQLITE_IOERR_READ; goto end_takeconch; }else if( readLen<=(PROXY_HEADERLEN+PROXY_HOSTIDLEN) || @@ -32043,7 +32349,7 @@ static int proxyTakeConch(unixFile *pFile){ rc = proxyConchLock(pFile, myHostID, EXCLUSIVE_LOCK); } }else{ - rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, EXCLUSIVE_LOCK); + rc = proxyConchLock(pFile, myHostID, EXCLUSIVE_LOCK); } if( rc==SQLITE_OK ){ char writeBuffer[PROXY_MAXCONCHLEN]; @@ -32052,7 +32358,8 @@ static int proxyTakeConch(unixFile *pFile){ writeBuffer[0] = (char)PROXY_CONCHVERSION; memcpy(&writeBuffer[PROXY_HEADERLEN], myHostID, PROXY_HOSTIDLEN); if( pCtx->lockProxyPath!=NULL ){ - strlcpy(&writeBuffer[PROXY_PATHINDEX], pCtx->lockProxyPath, MAXPATHLEN); + strlcpy(&writeBuffer[PROXY_PATHINDEX], pCtx->lockProxyPath, + MAXPATHLEN); }else{ strlcpy(&writeBuffer[PROXY_PATHINDEX], tempLockPath, MAXPATHLEN); } @@ -32164,7 +32471,7 @@ static int proxyReleaseConch(unixFile *pFile){ conchFile = pCtx->conchFile; OSTRACE(("RELEASECONCH %d for %s pid=%d\n", conchFile->h, (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"), - getpid())); + osGetpid(0))); if( pCtx->conchHeld>0 ){ rc = conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, NO_LOCK); } @@ -32176,7 +32483,7 @@ static int proxyReleaseConch(unixFile *pFile){ /* ** Given the name of a database file, compute the name of its conch file. -** Store the conch filename in memory obtained from sqlite3_malloc(). +** Store the conch filename in memory obtained from sqlite3_malloc64(). ** Make *pConchPath point to the new name. Return SQLITE_OK on success ** or SQLITE_NOMEM if unable to obtain memory. ** @@ -32192,7 +32499,7 @@ static int proxyCreateConchPathname(char *dbPath, char **pConchPath){ /* Allocate space for the conch filename and initialize the name to ** the name of the original database file. */ - *pConchPath = conchPath = (char *)sqlite3_malloc(len + 8); + *pConchPath = conchPath = (char *)sqlite3_malloc64(len + 8); if( conchPath==0 ){ return SQLITE_NOMEM; } @@ -32264,7 +32571,8 @@ static int proxyGetDbPathForUnixFile(unixFile *pFile, char *dbPath){ /* afp style keeps a reference to the db path in the filePath field ** of the struct */ assert( (int)strlen((char*)pFile->lockingContext)<=MAXPATHLEN ); - strlcpy(dbPath, ((afpLockingContext *)pFile->lockingContext)->dbPath, MAXPATHLEN); + strlcpy(dbPath, ((afpLockingContext *)pFile->lockingContext)->dbPath, + MAXPATHLEN); } else #endif if( pFile->pMethod == &dotlockIoMethods ){ @@ -32305,9 +32613,9 @@ static int proxyTransformUnixFile(unixFile *pFile, const char *path) { } OSTRACE(("TRANSPROXY %d for %s pid=%d\n", pFile->h, - (lockPath ? lockPath : ":auto:"), getpid())); + (lockPath ? lockPath : ":auto:"), osGetpid(0))); - pCtx = sqlite3_malloc( sizeof(*pCtx) ); + pCtx = sqlite3_malloc64( sizeof(*pCtx) ); if( pCtx==0 ){ return SQLITE_NOMEM; } @@ -32377,7 +32685,7 @@ static int proxyTransformUnixFile(unixFile *pFile, const char *path) { */ static int proxyFileControl(sqlite3_file *id, int op, void *pArg){ switch( op ){ - case SQLITE_GET_LOCKPROXYFILE: { + case SQLITE_FCNTL_GET_LOCKPROXYFILE: { unixFile *pFile = (unixFile*)id; if( pFile->pMethod == &proxyIoMethods ){ proxyLockingContext *pCtx = (proxyLockingContext*)pFile->lockingContext; @@ -32392,13 +32700,16 @@ static int proxyFileControl(sqlite3_file *id, int op, void *pArg){ } return SQLITE_OK; } - case SQLITE_SET_LOCKPROXYFILE: { + case SQLITE_FCNTL_SET_LOCKPROXYFILE: { unixFile *pFile = (unixFile*)id; int rc = SQLITE_OK; int isProxyStyle = (pFile->pMethod == &proxyIoMethods); if( pArg==NULL || (const char *)pArg==0 ){ if( isProxyStyle ){ - /* turn off proxy locking - not supported */ + /* turn off proxy locking - not supported. If support is added for + ** switching proxy locking mode off then it will need to fail if + ** the journal mode is WAL mode. + */ rc = SQLITE_ERROR /*SQLITE_PROTOCOL? SQLITE_MISUSE?*/; }else{ /* turn off proxy locking - already off - NOOP */ @@ -32589,7 +32900,7 @@ static int proxyClose(sqlite3_file *id) { ** necessarily been initialized when this routine is called, and so they ** should not be used. */ -SQLITE_API int sqlite3_os_init(void){ +SQLITE_API int SQLITE_STDCALL sqlite3_os_init(void){ /* ** The following macro defines an initializer for an sqlite3_vfs object. ** The name of the VFS is NAME. The pAppData is a pointer to a pointer @@ -32643,8 +32954,10 @@ SQLITE_API int sqlite3_os_init(void){ ** array cannot be const. */ static sqlite3_vfs aVfs[] = { -#if SQLITE_ENABLE_LOCKING_STYLE && (OS_VXWORKS || defined(__APPLE__)) +#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) UNIXVFS("unix", autolockIoFinder ), +#elif OS_VXWORKS + UNIXVFS("unix", vxworksIoFinder ), #else UNIXVFS("unix", posixIoFinder ), #endif @@ -32654,11 +32967,11 @@ SQLITE_API int sqlite3_os_init(void){ #if OS_VXWORKS UNIXVFS("unix-namedsem", semIoFinder ), #endif -#if SQLITE_ENABLE_LOCKING_STYLE +#if SQLITE_ENABLE_LOCKING_STYLE || OS_VXWORKS UNIXVFS("unix-posix", posixIoFinder ), -#if !OS_VXWORKS - UNIXVFS("unix-flock", flockIoFinder ), #endif +#if SQLITE_ENABLE_LOCKING_STYLE + UNIXVFS("unix-flock", flockIoFinder ), #endif #if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) UNIXVFS("unix-afp", afpIoFinder ), @@ -32686,7 +32999,7 @@ SQLITE_API int sqlite3_os_init(void){ ** to release dynamically allocated objects. But not on unix. ** This routine is a no-op for unix. */ -SQLITE_API int sqlite3_os_end(void){ +SQLITE_API int SQLITE_STDCALL sqlite3_os_end(void){ return SQLITE_OK; } @@ -32746,16 +33059,6 @@ SQLITE_API int sqlite3_os_end(void){ # error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." #endif -#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) -# ifndef SQLITE_DEBUG_OS_TRACE -# define SQLITE_DEBUG_OS_TRACE 0 -# endif - int sqlite3OSTrace = SQLITE_DEBUG_OS_TRACE; -# define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X -#else -# define OSTRACE(X) -#endif - /* ** Macros for performance tracing. Normally turned off. Only works ** on i486 hardware. @@ -33099,8 +33402,10 @@ WINBASEAPI LPVOID WINAPI MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, SIZE_T); #endif /* SQLITE_OS_WINRT */ /* -** This file mapping API is common to both Win32 and WinRT. +** These file mapping APIs are common to both Win32 and WinRT. */ + +WINBASEAPI BOOL WINAPI FlushViewOfFile(LPCVOID, SIZE_T); WINBASEAPI BOOL WINAPI UnmapViewOfFile(LPCVOID); #endif /* SQLITE_WIN32_FILEMAPPING_API */ @@ -33968,6 +34273,32 @@ static struct win_syscall { SQLITE_WIN32_VOLATILE*, LONG,LONG))aSyscall[76].pCurrent) #endif /* defined(InterlockedCompareExchange) */ +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID + { "UuidCreate", (SYSCALL)UuidCreate, 0 }, +#else + { "UuidCreate", (SYSCALL)0, 0 }, +#endif + +#define osUuidCreate ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[77].pCurrent) + +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID + { "UuidCreateSequential", (SYSCALL)UuidCreateSequential, 0 }, +#else + { "UuidCreateSequential", (SYSCALL)0, 0 }, +#endif + +#define osUuidCreateSequential \ + ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[78].pCurrent) + +#if !defined(SQLITE_NO_SYNC) && SQLITE_MAX_MMAP_SIZE>0 + { "FlushViewOfFile", (SYSCALL)FlushViewOfFile, 0 }, +#else + { "FlushViewOfFile", (SYSCALL)0, 0 }, +#endif + +#define osFlushViewOfFile \ + ((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[79].pCurrent) + }; /* End of the overrideable system calls */ /* @@ -34061,7 +34392,7 @@ static const char *winNextSystemCall(sqlite3_vfs *p, const char *zName){ ** "pnLargest" argument, if non-zero, will be used to return the size of the ** largest committed free block in the heap, in bytes. */ -SQLITE_API int sqlite3_win32_compact_heap(LPUINT pnLargest){ +SQLITE_API int SQLITE_STDCALL sqlite3_win32_compact_heap(LPUINT pnLargest){ int rc = SQLITE_OK; UINT nLargest = 0; HANDLE hHeap; @@ -34101,7 +34432,7 @@ SQLITE_API int sqlite3_win32_compact_heap(LPUINT pnLargest){ ** the sqlite3_memory_used() function does not return zero, SQLITE_BUSY will ** be returned and no changes will be made to the Win32 native heap. */ -SQLITE_API int sqlite3_win32_reset_heap(){ +SQLITE_API int SQLITE_STDCALL sqlite3_win32_reset_heap(){ int rc; MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */ MUTEX_LOGIC( sqlite3_mutex *pMem; ) /* The memsys static mutex */ @@ -34146,7 +34477,7 @@ SQLITE_API int sqlite3_win32_reset_heap(){ ** (if available). */ -SQLITE_API void sqlite3_win32_write_debug(const char *zBuf, int nBuf){ +SQLITE_API void SQLITE_STDCALL sqlite3_win32_write_debug(const char *zBuf, int nBuf){ char zDbgBuf[SQLITE_WIN32_DBG_BUF_SIZE]; int nMin = MIN(nBuf, (SQLITE_WIN32_DBG_BUF_SIZE - 1)); /* may be negative. */ if( nMin<-1 ) nMin = -1; /* all negative values become -1. */ @@ -34186,7 +34517,7 @@ SQLITE_API void sqlite3_win32_write_debug(const char *zBuf, int nBuf){ static HANDLE sleepObj = NULL; #endif -SQLITE_API void sqlite3_win32_sleep(DWORD milliseconds){ +SQLITE_API void SQLITE_STDCALL sqlite3_win32_sleep(DWORD milliseconds){ #if SQLITE_OS_WINRT if ( sleepObj==NULL ){ sleepObj = osCreateEventExW(NULL, NULL, CREATE_EVENT_MANUAL_RESET, @@ -34235,7 +34566,7 @@ SQLITE_PRIVATE DWORD sqlite3Win32Wait(HANDLE hObject){ ** This function determines if the machine is running a version of Windows ** based on the NT kernel. */ -SQLITE_API int sqlite3_win32_is_nt(void){ +SQLITE_API int SQLITE_STDCALL sqlite3_win32_is_nt(void){ #if SQLITE_OS_WINRT /* ** NOTE: The WinRT sub-platform is always assumed to be based on the NT @@ -34589,7 +34920,7 @@ static char *winUnicodeToMbcs(LPCWSTR zWideFilename){ ** Convert multibyte character string to UTF-8. Space to hold the ** returned string is obtained from sqlite3_malloc(). */ -SQLITE_API char *sqlite3_win32_mbcs_to_utf8(const char *zFilename){ +SQLITE_API char *SQLITE_STDCALL sqlite3_win32_mbcs_to_utf8(const char *zFilename){ char *zFilenameUtf8; LPWSTR zTmpWide; @@ -34606,7 +34937,7 @@ SQLITE_API char *sqlite3_win32_mbcs_to_utf8(const char *zFilename){ ** Convert UTF-8 to multibyte character string. Space to hold the ** returned string is obtained from sqlite3_malloc(). */ -SQLITE_API char *sqlite3_win32_utf8_to_mbcs(const char *zFilename){ +SQLITE_API char *SQLITE_STDCALL sqlite3_win32_utf8_to_mbcs(const char *zFilename){ char *zFilenameMbcs; LPWSTR zTmpWide; @@ -34626,7 +34957,7 @@ SQLITE_API char *sqlite3_win32_utf8_to_mbcs(const char *zFilename){ ** argument is the name of the directory to use. The return value will be ** SQLITE_OK if successful. */ -SQLITE_API int sqlite3_win32_set_directory(DWORD type, LPCWSTR zValue){ +SQLITE_API int SQLITE_STDCALL sqlite3_win32_set_directory(DWORD type, LPCWSTR zValue){ char **ppDirectory = 0; #ifndef SQLITE_OMIT_AUTOINIT int rc = sqlite3_initialize(); @@ -34851,11 +35182,11 @@ static int winRetryIoerr(int *pnRetry, DWORD *pError){ /* ** Log a I/O error retry episode. */ -static void winLogIoerr(int nRetry){ +static void winLogIoerr(int nRetry, int lineno){ if( nRetry ){ - sqlite3_log(SQLITE_IOERR, - "delayed %dms for lock/sharing conflict", - winIoerrRetryDelay*nRetry*(nRetry+1)/2 + sqlite3_log(SQLITE_NOTICE, + "delayed %dms for lock/sharing conflict at line %d", + winIoerrRetryDelay*nRetry*(nRetry+1)/2, lineno ); } } @@ -35335,7 +35666,8 @@ static int winClose(sqlite3_file *id){ assert( pFile->pShm==0 ); #endif assert( pFile->h!=NULL && pFile->h!=INVALID_HANDLE_VALUE ); - OSTRACE(("CLOSE file=%p\n", pFile->h)); + OSTRACE(("CLOSE pid=%lu, pFile=%p, file=%p\n", + osGetCurrentProcessId(), pFile, pFile->h)); #if SQLITE_MAX_MMAP_SIZE>0 winUnmapfile(pFile); @@ -35364,7 +35696,8 @@ static int winClose(sqlite3_file *id){ pFile->h = NULL; } OpenCounter(-1); - OSTRACE(("CLOSE file=%p, rc=%s\n", pFile->h, rc ? "ok" : "failed")); + OSTRACE(("CLOSE pid=%lu, pFile=%p, file=%p, rc=%s\n", + osGetCurrentProcessId(), pFile, pFile->h, rc ? "ok" : "failed")); return rc ? SQLITE_OK : winLogError(SQLITE_IOERR_CLOSE, osGetLastError(), "winClose", pFile->zPath); @@ -35392,7 +35725,8 @@ static int winRead( assert( amt>0 ); assert( offset>=0 ); SimulateIOError(return SQLITE_IOERR_READ); - OSTRACE(("READ file=%p, buffer=%p, amount=%d, offset=%lld, lock=%d\n", + OSTRACE(("READ pid=%lu, pFile=%p, file=%p, buffer=%p, amount=%d, " + "offset=%lld, lock=%d\n", osGetCurrentProcessId(), pFile, pFile->h, pBuf, amt, offset, pFile->locktype)); #if SQLITE_MAX_MMAP_SIZE>0 @@ -35401,7 +35735,8 @@ static int winRead( if( offsetmmapSize ){ if( offset+amt <= pFile->mmapSize ){ memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], amt); - OSTRACE(("READ-MMAP file=%p, rc=SQLITE_OK\n", pFile->h)); + OSTRACE(("READ-MMAP pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), pFile, pFile->h)); return SQLITE_OK; }else{ int nCopy = (int)(pFile->mmapSize - offset); @@ -35415,7 +35750,8 @@ static int winRead( #if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED) if( winSeekFile(pFile, offset) ){ - OSTRACE(("READ file=%p, rc=SQLITE_FULL\n", pFile->h)); + OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_FULL\n", + osGetCurrentProcessId(), pFile, pFile->h)); return SQLITE_FULL; } while( !osReadFile(pFile->h, pBuf, amt, &nRead, 0) ){ @@ -35429,19 +35765,22 @@ static int winRead( DWORD lastErrno; if( winRetryIoerr(&nRetry, &lastErrno) ) continue; pFile->lastErrno = lastErrno; - OSTRACE(("READ file=%p, rc=SQLITE_IOERR_READ\n", pFile->h)); + OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_READ\n", + osGetCurrentProcessId(), pFile, pFile->h)); return winLogError(SQLITE_IOERR_READ, pFile->lastErrno, "winRead", pFile->zPath); } - winLogIoerr(nRetry); + winLogIoerr(nRetry, __LINE__); if( nRead<(DWORD)amt ){ /* Unread parts of the buffer must be zero-filled */ memset(&((char*)pBuf)[nRead], 0, amt-nRead); - OSTRACE(("READ file=%p, rc=SQLITE_IOERR_SHORT_READ\n", pFile->h)); + OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_SHORT_READ\n", + osGetCurrentProcessId(), pFile, pFile->h)); return SQLITE_IOERR_SHORT_READ; } - OSTRACE(("READ file=%p, rc=SQLITE_OK\n", pFile->h)); + OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), pFile, pFile->h)); return SQLITE_OK; } @@ -35464,7 +35803,8 @@ static int winWrite( SimulateIOError(return SQLITE_IOERR_WRITE); SimulateDiskfullError(return SQLITE_FULL); - OSTRACE(("WRITE file=%p, buffer=%p, amount=%d, offset=%lld, lock=%d\n", + OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, buffer=%p, amount=%d, " + "offset=%lld, lock=%d\n", osGetCurrentProcessId(), pFile, pFile->h, pBuf, amt, offset, pFile->locktype)); #if SQLITE_MAX_MMAP_SIZE>0 @@ -35473,7 +35813,8 @@ static int winWrite( if( offsetmmapSize ){ if( offset+amt <= pFile->mmapSize ){ memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, amt); - OSTRACE(("WRITE-MMAP file=%p, rc=SQLITE_OK\n", pFile->h)); + OSTRACE(("WRITE-MMAP pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), pFile, pFile->h)); return SQLITE_OK; }else{ int nCopy = (int)(pFile->mmapSize - offset); @@ -35536,17 +35877,20 @@ static int winWrite( if( rc ){ if( ( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ) || ( pFile->lastErrno==ERROR_DISK_FULL )){ - OSTRACE(("WRITE file=%p, rc=SQLITE_FULL\n", pFile->h)); + OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_FULL\n", + osGetCurrentProcessId(), pFile, pFile->h)); return winLogError(SQLITE_FULL, pFile->lastErrno, "winWrite1", pFile->zPath); } - OSTRACE(("WRITE file=%p, rc=SQLITE_IOERR_WRITE\n", pFile->h)); + OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_WRITE\n", + osGetCurrentProcessId(), pFile, pFile->h)); return winLogError(SQLITE_IOERR_WRITE, pFile->lastErrno, "winWrite2", pFile->zPath); }else{ - winLogIoerr(nRetry); + winLogIoerr(nRetry, __LINE__); } - OSTRACE(("WRITE file=%p, rc=SQLITE_OK\n", pFile->h)); + OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), pFile, pFile->h)); return SQLITE_OK; } @@ -35560,8 +35904,8 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ assert( pFile ); SimulateIOError(return SQLITE_IOERR_TRUNCATE); - OSTRACE(("TRUNCATE file=%p, size=%lld, lock=%d\n", - pFile->h, nByte, pFile->locktype)); + OSTRACE(("TRUNCATE pid=%lu, pFile=%p, file=%p, size=%lld, lock=%d\n", + osGetCurrentProcessId(), pFile, pFile->h, nByte, pFile->locktype)); /* If the user has configured a chunk-size for this file, truncate the ** file so that it consists of an integer number of chunks (i.e. the @@ -35593,7 +35937,8 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ } #endif - OSTRACE(("TRUNCATE file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc))); + OSTRACE(("TRUNCATE pid=%lu, pFile=%p, file=%p, rc=%s\n", + osGetCurrentProcessId(), pFile, pFile->h, sqlite3ErrName(rc))); return rc; } @@ -35617,7 +35962,7 @@ static int winSync(sqlite3_file *id, int flags){ BOOL rc; #endif #if !defined(NDEBUG) || !defined(SQLITE_NO_SYNC) || \ - (defined(SQLITE_TEST) && defined(SQLITE_DEBUG)) + defined(SQLITE_HAVE_OS_TRACE) /* ** Used when SQLITE_NO_SYNC is not defined and by the assert() and/or ** OSTRACE() macros. @@ -35638,8 +35983,9 @@ static int winSync(sqlite3_file *id, int flags){ */ SimulateDiskfullError( return SQLITE_FULL ); - OSTRACE(("SYNC file=%p, flags=%x, lock=%d\n", - pFile->h, flags, pFile->locktype)); + OSTRACE(("SYNC pid=%lu, pFile=%p, file=%p, flags=%x, lock=%d\n", + osGetCurrentProcessId(), pFile, pFile->h, flags, + pFile->locktype)); #ifndef SQLITE_TEST UNUSED_PARAMETER(flags); @@ -35654,19 +36000,38 @@ static int winSync(sqlite3_file *id, int flags){ ** no-op */ #ifdef SQLITE_NO_SYNC - OSTRACE(("SYNC-NOP file=%p, rc=SQLITE_OK\n", pFile->h)); + OSTRACE(("SYNC-NOP pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), pFile, pFile->h)); return SQLITE_OK; #else +#if SQLITE_MAX_MMAP_SIZE>0 + if( pFile->pMapRegion ){ + if( osFlushViewOfFile(pFile->pMapRegion, 0) ){ + OSTRACE(("SYNC-MMAP pid=%lu, pFile=%p, pMapRegion=%p, " + "rc=SQLITE_OK\n", osGetCurrentProcessId(), + pFile, pFile->pMapRegion)); + }else{ + pFile->lastErrno = osGetLastError(); + OSTRACE(("SYNC-MMAP pid=%lu, pFile=%p, pMapRegion=%p, " + "rc=SQLITE_IOERR_MMAP\n", osGetCurrentProcessId(), + pFile, pFile->pMapRegion)); + return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno, + "winSync1", pFile->zPath); + } + } +#endif rc = osFlushFileBuffers(pFile->h); SimulateIOError( rc=FALSE ); if( rc ){ - OSTRACE(("SYNC file=%p, rc=SQLITE_OK\n", pFile->h)); + OSTRACE(("SYNC pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), pFile, pFile->h)); return SQLITE_OK; }else{ pFile->lastErrno = osGetLastError(); - OSTRACE(("SYNC file=%p, rc=SQLITE_IOERR_FSYNC\n", pFile->h)); + OSTRACE(("SYNC pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_FSYNC\n", + osGetCurrentProcessId(), pFile, pFile->h)); return winLogError(SQLITE_IOERR_FSYNC, pFile->lastErrno, - "winSync", pFile->zPath); + "winSync2", pFile->zPath); } #endif } @@ -36274,7 +36639,7 @@ struct winShmNode { int nRef; /* Number of winShm objects pointing to this */ winShm *pFirst; /* All winShm objects pointing to this */ winShmNode *pNext; /* Next in list of all winShmNode objects */ -#ifdef SQLITE_DEBUG +#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) u8 nextShmId; /* Next available winShm.id value */ #endif }; @@ -36305,7 +36670,7 @@ struct winShm { u8 hasMutex; /* True if holding the winShmNode mutex */ u16 sharedMask; /* Mask of shared locks held */ u16 exclMask; /* Mask of exclusive locks held */ -#ifdef SQLITE_DEBUG +#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) u8 id; /* Id of this connection with its winShmNode */ #endif }; @@ -36496,7 +36861,7 @@ static int winOpenSharedMemory(winFile *pDbFd){ /* Make the new connection a child of the winShmNode */ p->pShmNode = pShmNode; -#ifdef SQLITE_DEBUG +#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) p->id = pShmNode->nextShmId++; #endif pShmNode->nRef++; @@ -36716,16 +37081,16 @@ static int winShmMap( void volatile **pp /* OUT: Mapped memory */ ){ winFile *pDbFd = (winFile*)fd; - winShm *p = pDbFd->pShm; + winShm *pShm = pDbFd->pShm; winShmNode *pShmNode; int rc = SQLITE_OK; - if( !p ){ + if( !pShm ){ rc = winOpenSharedMemory(pDbFd); if( rc!=SQLITE_OK ) return rc; - p = pDbFd->pShm; + pShm = pDbFd->pShm; } - pShmNode = p->pShmNode; + pShmNode = pShm->pShmNode; sqlite3_mutex_enter(pShmNode->mutex); assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); @@ -36765,7 +37130,7 @@ static int winShmMap( } /* Map the requested memory region into this processes address space. */ - apNew = (struct ShmRegion *)sqlite3_realloc( + apNew = (struct ShmRegion *)sqlite3_realloc64( pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0]) ); if( !apNew ){ @@ -37637,7 +38002,7 @@ static int winOpen( } } #endif - winLogIoerr(cnt); + winLogIoerr(cnt, __LINE__); OSTRACE(("OPEN file=%p, name=%s, access=%lx, rc=%s\n", h, zUtf8Name, dwDesiredAccess, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok")); @@ -37821,7 +38186,7 @@ static int winDelete( if( rc && rc!=SQLITE_IOERR_DELETE_NOENT ){ rc = winLogError(SQLITE_IOERR_DELETE, lastErrno, "winDelete", zFilename); }else{ - winLogIoerr(cnt); + winLogIoerr(cnt, __LINE__); } sqlite3_free(zConverted); OSTRACE(("DELETE name=%s, rc=%s\n", zFilename, sqlite3ErrName(rc))); @@ -37871,7 +38236,7 @@ static int winAccess( attr = sAttrData.dwFileAttributes; } }else{ - winLogIoerr(cnt); + winLogIoerr(cnt, __LINE__); if( lastErrno!=ERROR_FILE_NOT_FOUND && lastErrno!=ERROR_PATH_NOT_FOUND ){ sqlite3_free(zConverted); return winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess", @@ -38212,7 +38577,7 @@ static void winDlClose(sqlite3_vfs *pVfs, void *pHandle){ static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ int n = 0; UNUSED_PARAMETER(pVfs); -#if defined(SQLITE_TEST) +#if defined(SQLITE_TEST) || defined(SQLITE_OMIT_RANDOMNESS) n = nBuf; memset(zBuf, 0, nBuf); #else @@ -38246,7 +38611,23 @@ static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ memcpy(&zBuf[n], &i, sizeof(i)); n += sizeof(i); } +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID + if( sizeof(UUID)<=nBuf-n ){ + UUID id; + memset(&id, 0, sizeof(UUID)); + osUuidCreate(&id); + memcpy(zBuf, &id, sizeof(UUID)); + n += sizeof(UUID); + } + if( sizeof(UUID)<=nBuf-n ){ + UUID id; + memset(&id, 0, sizeof(UUID)); + osUuidCreateSequential(&id); + memcpy(zBuf, &id, sizeof(UUID)); + n += sizeof(UUID); + } #endif +#endif /* defined(SQLITE_TEST) || defined(SQLITE_ZERO_PRNG_SEED) */ return n; } @@ -38370,7 +38751,7 @@ static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ /* ** Initialize and deinitialize the operating system interface. */ -SQLITE_API int sqlite3_os_init(void){ +SQLITE_API int SQLITE_STDCALL sqlite3_os_init(void){ static sqlite3_vfs winVfs = { 3, /* iVersion */ sizeof(winFile), /* szOsFile */ @@ -38424,7 +38805,7 @@ SQLITE_API int sqlite3_os_init(void){ /* Double-check that the aSyscall[] array has been constructed ** correctly. See ticket [bb3a86e890c8e96ab] */ - assert( ArraySize(aSyscall)==77 ); + assert( ArraySize(aSyscall)==80 ); /* get memory map allocation granularity */ memset(&winSysInfo, 0, sizeof(SYSTEM_INFO)); @@ -38445,7 +38826,7 @@ SQLITE_API int sqlite3_os_init(void){ return SQLITE_OK; } -SQLITE_API int sqlite3_os_end(void){ +SQLITE_API int SQLITE_STDCALL sqlite3_os_end(void){ #if SQLITE_OS_WINRT if( sleepObj!=NULL ){ osCloseHandle(sleepObj); @@ -38801,7 +39182,7 @@ SQLITE_PRIVATE int sqlite3BitvecBuiltinTest(int sz, int *aOp){ ** bits to act as the reference */ pBitvec = sqlite3BitvecCreate( sz ); pV = sqlite3MallocZero( (sz+7)/8 + 1 ); - pTmpSpace = sqlite3_malloc(BITVEC_SZ); + pTmpSpace = sqlite3_malloc64(BITVEC_SZ); if( pBitvec==0 || pV==0 || pTmpSpace==0 ) goto bitvec_end; /* NULL pBitvec tests */ @@ -38983,12 +39364,20 @@ static void pcacheUnpin(PgHdr *p){ } /* -** Compute the number of pages of cache requested. +** Compute the number of pages of cache requested. p->szCache is the +** cache size requested by the "PRAGMA cache_size" statement. +** +** */ static int numberOfCachePages(PCache *p){ if( p->szCache>=0 ){ + /* IMPLEMENTATION-OF: R-42059-47211 If the argument N is positive then the + ** suggested cache size is set to N. */ return p->szCache; }else{ + /* IMPLEMENTATION-OF: R-61436-13639 If the argument N is negative, then + ** the number of cache pages is adjusted to use approximately abs(N*1024) + ** bytes of memory. */ return (int)((-1024*(i64)p->szCache)/(p->szPage+p->szExtra)); } } @@ -39728,7 +40117,6 @@ SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){ static void *pcache1Alloc(int nByte){ void *p = 0; assert( sqlite3_mutex_notheld(pcache1.grp.mutex) ); - sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, nByte); if( nByte<=pcache1.szSlot ){ sqlite3_mutex_enter(pcache1.mutex); p = (PgHdr1 *)pcache1.pFree; @@ -39737,7 +40125,8 @@ static void *pcache1Alloc(int nByte){ pcache1.nFreeSlot--; pcache1.bUnderPressure = pcache1.nFreeSlot=0 ); - sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1); + sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, nByte); + sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1); } sqlite3_mutex_leave(pcache1.mutex); } @@ -39750,7 +40139,8 @@ static void *pcache1Alloc(int nByte){ if( p ){ int sz = sqlite3MallocSize(p); sqlite3_mutex_enter(pcache1.mutex); - sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz); + sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, nByte); + sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz); sqlite3_mutex_leave(pcache1.mutex); } #endif @@ -39768,7 +40158,7 @@ static int pcache1Free(void *p){ if( p>=pcache1.pStart && ppNext = pcache1.pFree; pcache1.pFree = pSlot; @@ -39782,7 +40172,7 @@ static int pcache1Free(void *p){ nFreed = sqlite3MallocSize(p); #ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS sqlite3_mutex_enter(pcache1.mutex); - sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -nFreed); + sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_OVERFLOW, nFreed); sqlite3_mutex_leave(pcache1.mutex); #endif sqlite3_free(p); @@ -40519,6 +40909,14 @@ SQLITE_PRIVATE void sqlite3PCacheSetDefault(void){ */ SQLITE_PRIVATE int sqlite3HeaderSizePcache1(void){ return ROUND8(sizeof(PgHdr1)); } +/* +** Return the global mutex used by this PCACHE implementation. The +** sqlite3_status() routine needs access to this mutex. +*/ +SQLITE_PRIVATE sqlite3_mutex *sqlite3Pcache1Mutex(void){ + return pcache1.mutex; +} + #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT /* ** This function is called to free superfluous dynamically allocated memory @@ -44273,9 +44671,7 @@ static int pagerWalFrames( ){ int rc; /* Return code */ int nList; /* Number of pages in pList */ -#if defined(SQLITE_DEBUG) || defined(SQLITE_CHECK_PAGES) PgHdr *p; /* For looping over pages */ -#endif assert( pPager->pWal ); assert( pList ); @@ -44292,7 +44688,6 @@ static int pagerWalFrames( ** any pages with page numbers greater than nTruncate into the WAL file. ** They will never be read by any client. So remove them from the pDirty ** list here. */ - PgHdr *p; PgHdr **ppNext = &pList; nList = 0; for(p=pList; (*ppNext = p)!=0; p=p->pDirty){ @@ -44312,7 +44707,6 @@ static int pagerWalFrames( pPager->pageSize, pList, nTruncate, isCommit, pPager->walSyncFlags ); if( rc==SQLITE_OK && pPager->pBackup ){ - PgHdr *p; for(p=pList; p; p=p->pDirty){ sqlite3BackupUpdate(pPager->pBackup, p->pgno, (u8 *)p->pData); } @@ -48243,6 +48637,8 @@ SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){ } assert( state==pPager->eState ); } + }else if( eMode==PAGER_JOURNALMODE_OFF ){ + sqlite3OsClose(pPager->jfd); } } @@ -49025,7 +49421,7 @@ static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){ if( pWal->nWiData<=iPage ){ int nByte = sizeof(u32*)*(iPage+1); volatile u32 **apNew; - apNew = (volatile u32 **)sqlite3_realloc((void *)pWal->apWiData, nByte); + apNew = (volatile u32 **)sqlite3_realloc64((void *)pWal->apWiData, nByte); if( !apNew ){ *ppPage = 0; return SQLITE_NOMEM; @@ -49291,9 +49687,10 @@ static void walUnlockShared(Wal *pWal, int lockIdx){ SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED); WALTRACE(("WAL%p: release SHARED-%s\n", pWal, walLockName(lockIdx))); } -static int walLockExclusive(Wal *pWal, int lockIdx, int n){ +static int walLockExclusive(Wal *pWal, int lockIdx, int n, int fBlock){ int rc; if( pWal->exclusiveMode ) return SQLITE_OK; + if( fBlock ) sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_WAL_BLOCK, 0); rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE); WALTRACE(("WAL%p: acquire EXCLUSIVE-%s cnt=%d %s\n", pWal, @@ -49579,7 +49976,7 @@ static int walIndexRecover(Wal *pWal){ assert( pWal->writeLock ); iLock = WAL_ALL_BUT_WRITE + pWal->ckptLock; nLock = SQLITE_SHM_NLOCK - iLock; - rc = walLockExclusive(pWal, iLock, nLock); + rc = walLockExclusive(pWal, iLock, nLock, 0); if( rc ){ return rc; } @@ -49649,7 +50046,7 @@ static int walIndexRecover(Wal *pWal){ /* Malloc a buffer to read frames into. */ szFrame = szPage + WAL_FRAME_HDRSIZE; - aFrame = (u8 *)sqlite3_malloc(szFrame); + aFrame = (u8 *)sqlite3_malloc64(szFrame); if( !aFrame ){ rc = SQLITE_NOMEM; goto recovery_error; @@ -50042,7 +50439,7 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){ nByte = sizeof(WalIterator) + (nSegment-1)*sizeof(struct WalSegment) + iLast*sizeof(ht_slot); - p = (WalIterator *)sqlite3_malloc(nByte); + p = (WalIterator *)sqlite3_malloc64(nByte); if( !p ){ return SQLITE_NOMEM; } @@ -50052,7 +50449,7 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){ /* Allocate temporary space used by the merge-sort routine. This block ** of memory will be freed before this function returns. */ - aTmp = (ht_slot *)sqlite3_malloc( + aTmp = (ht_slot *)sqlite3_malloc64( sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast) ); if( !aTmp ){ @@ -50113,7 +50510,7 @@ static int walBusyLock( ){ int rc; do { - rc = walLockExclusive(pWal, lockIdx, n); + rc = walLockExclusive(pWal, lockIdx, n, 0); }while( xBusy && rc==SQLITE_BUSY && xBusy(pBusyArg) ); return rc; } @@ -50197,7 +50594,7 @@ static int walCheckpoint( int sync_flags, /* Flags for OsSync() (or 0) */ u8 *zBuf /* Temporary buffer to use */ ){ - int rc; /* Return code */ + int rc = SQLITE_OK; /* Return code */ int szPage; /* Database page-size */ WalIterator *pIter = 0; /* Wal iterator context */ u32 iDbpage = 0; /* Next database page to write */ @@ -50211,104 +50608,115 @@ static int walCheckpoint( testcase( szPage<=32768 ); testcase( szPage>=65536 ); pInfo = walCkptInfo(pWal); - if( pInfo->nBackfill>=pWal->hdr.mxFrame ) return SQLITE_OK; + if( pInfo->nBackfillhdr.mxFrame ){ - /* Allocate the iterator */ - rc = walIteratorInit(pWal, &pIter); - if( rc!=SQLITE_OK ){ - return rc; - } - assert( pIter ); - - /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked - ** in the SQLITE_CHECKPOINT_PASSIVE mode. */ - assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 ); - - /* Compute in mxSafeFrame the index of the last frame of the WAL that is - ** safe to write into the database. Frames beyond mxSafeFrame might - ** overwrite database pages that are in use by active readers and thus - ** cannot be backfilled from the WAL. - */ - mxSafeFrame = pWal->hdr.mxFrame; - mxPage = pWal->hdr.nPage; - for(i=1; iaReadMark[i]; - if( mxSafeFrame>y ){ - assert( y<=pWal->hdr.mxFrame ); - rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1); - if( rc==SQLITE_OK ){ - pInfo->aReadMark[i] = (i==1 ? mxSafeFrame : READMARK_NOT_USED); - walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); - }else if( rc==SQLITE_BUSY ){ - mxSafeFrame = y; - xBusy = 0; - }else{ - goto walcheckpoint_out; - } + /* Allocate the iterator */ + rc = walIteratorInit(pWal, &pIter); + if( rc!=SQLITE_OK ){ + return rc; } - } + assert( pIter ); - if( pInfo->nBackfillnBackfill; + /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked + ** in the SQLITE_CHECKPOINT_PASSIVE mode. */ + assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 ); - /* Sync the WAL to disk */ - if( sync_flags ){ - rc = sqlite3OsSync(pWal->pWalFd, sync_flags); - } - - /* If the database may grow as a result of this checkpoint, hint - ** about the eventual size of the db file to the VFS layer. + /* Compute in mxSafeFrame the index of the last frame of the WAL that is + ** safe to write into the database. Frames beyond mxSafeFrame might + ** overwrite database pages that are in use by active readers and thus + ** cannot be backfilled from the WAL. */ - if( rc==SQLITE_OK ){ - i64 nReq = ((i64)mxPage * szPage); - rc = sqlite3OsFileSize(pWal->pDbFd, &nSize); - if( rc==SQLITE_OK && nSizepDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq); - } - } - - - /* Iterate through the contents of the WAL, copying data to the db file. */ - while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ - i64 iOffset; - assert( walFramePgno(pWal, iFrame)==iDbpage ); - if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ) continue; - iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE; - /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */ - rc = sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset); - if( rc!=SQLITE_OK ) break; - iOffset = (iDbpage-1)*(i64)szPage; - testcase( IS_BIG_INT(iOffset) ); - rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset); - if( rc!=SQLITE_OK ) break; - } - - /* If work was actually accomplished... */ - if( rc==SQLITE_OK ){ - if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){ - i64 szDb = pWal->hdr.nPage*(i64)szPage; - testcase( IS_BIG_INT(szDb) ); - rc = sqlite3OsTruncate(pWal->pDbFd, szDb); - if( rc==SQLITE_OK && sync_flags ){ - rc = sqlite3OsSync(pWal->pDbFd, sync_flags); + mxSafeFrame = pWal->hdr.mxFrame; + mxPage = pWal->hdr.nPage; + for(i=1; iaReadMark[i]; + if( mxSafeFrame>y ){ + assert( y<=pWal->hdr.mxFrame ); + rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1); + if( rc==SQLITE_OK ){ + pInfo->aReadMark[i] = (i==1 ? mxSafeFrame : READMARK_NOT_USED); + walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); + }else if( rc==SQLITE_BUSY ){ + mxSafeFrame = y; + xBusy = 0; + }else{ + goto walcheckpoint_out; } } - if( rc==SQLITE_OK ){ - pInfo->nBackfill = mxSafeFrame; - } } - /* Release the reader lock held while backfilling */ - walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1); - } + if( pInfo->nBackfillnBackfill; - if( rc==SQLITE_BUSY ){ - /* Reset the return code so as not to report a checkpoint failure - ** just because there are active readers. */ - rc = SQLITE_OK; + /* Sync the WAL to disk */ + if( sync_flags ){ + rc = sqlite3OsSync(pWal->pWalFd, sync_flags); + } + + /* If the database may grow as a result of this checkpoint, hint + ** about the eventual size of the db file to the VFS layer. + */ + if( rc==SQLITE_OK ){ + i64 nReq = ((i64)mxPage * szPage); + rc = sqlite3OsFileSize(pWal->pDbFd, &nSize); + if( rc==SQLITE_OK && nSizepDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq); + } + } + + + /* Iterate through the contents of the WAL, copying data to the db file */ + while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ + i64 iOffset; + assert( walFramePgno(pWal, iFrame)==iDbpage ); + if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ){ + continue; + } + iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE; + /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */ + rc = sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset); + if( rc!=SQLITE_OK ) break; + iOffset = (iDbpage-1)*(i64)szPage; + testcase( IS_BIG_INT(iOffset) ); + rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset); + if( rc!=SQLITE_OK ) break; + } + + /* If work was actually accomplished... */ + if( rc==SQLITE_OK ){ + if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){ + i64 szDb = pWal->hdr.nPage*(i64)szPage; + testcase( IS_BIG_INT(szDb) ); + rc = sqlite3OsTruncate(pWal->pDbFd, szDb); + if( rc==SQLITE_OK && sync_flags ){ + rc = sqlite3OsSync(pWal->pDbFd, sync_flags); + } + } + if( rc==SQLITE_OK ){ + pInfo->nBackfill = mxSafeFrame; + } + } + + /* Release the reader lock held while backfilling */ + walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1); + } + + if( rc==SQLITE_BUSY ){ + /* Reset the return code so as not to report a checkpoint failure + ** just because there are active readers. */ + rc = SQLITE_OK; + } } /* If this is an SQLITE_CHECKPOINT_RESTART or TRUNCATE operation, and the @@ -50323,7 +50731,7 @@ static int walCheckpoint( }else if( eMode>=SQLITE_CHECKPOINT_RESTART ){ u32 salt1; sqlite3_randomness(4, &salt1); - assert( mxSafeFrame==pWal->hdr.mxFrame ); + assert( pInfo->nBackfill==pWal->hdr.mxFrame ); rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1); if( rc==SQLITE_OK ){ if( eMode==SQLITE_CHECKPOINT_TRUNCATE ){ @@ -50543,7 +50951,7 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){ walUnlockShared(pWal, WAL_WRITE_LOCK); rc = SQLITE_READONLY_RECOVERY; } - }else if( SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) ){ + }else if( SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1, 1)) ){ pWal->writeLock = 1; if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){ badHdr = walIndexTryHdr(pWal, pChanged); @@ -50749,7 +51157,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ && (mxReadMarkhdr.mxFrame || mxI==0) ){ for(i=1; iaReadMark[i] = pWal->hdr.mxFrame; mxI = i; @@ -51005,7 +51413,7 @@ SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal){ /* Only one writer allowed at a time. Get the write lock. Return ** SQLITE_BUSY if unable. */ - rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1); + rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1, 0); if( rc ){ return rc; } @@ -51150,7 +51558,7 @@ static int walRestartLog(Wal *pWal){ if( pInfo->nBackfill>0 ){ u32 salt1; sqlite3_randomness(4, &salt1); - rc = walLockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); + rc = walLockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1, 0); if( rc==SQLITE_OK ){ /* If all readers are using WAL_READ_LOCK(0) (in other words if no ** readers are currently using the WAL), then the transactions @@ -51475,7 +51883,7 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint( /* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive ** "checkpoint" lock on the database file. */ - rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); + rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1, 0); if( rc ){ /* EVIDENCE-OF: R-10421-19736 If any other process is running a ** checkpoint operation at the same time, the lock cannot be obtained and @@ -51950,6 +52358,7 @@ struct MemPage { u8 hdrOffset; /* 100 for page 1. 0 otherwise */ u8 childPtrSize; /* 0 if leaf==1. 4 if leaf==0 */ u8 max1bytePayload; /* min(maxLocal,127) */ + u8 bBusy; /* Prevent endless loops on corrupt database files */ u16 maxLocal; /* Copy of BtShared.maxLocal or BtShared.maxLeaf */ u16 minLocal; /* Copy of BtShared.minLocal or BtShared.minLeaf */ u16 cellOffset; /* Index in aData of first cell pointer */ @@ -52088,6 +52497,9 @@ struct BtShared { #endif u8 inTransaction; /* Transaction state */ u8 max1bytePayload; /* Maximum first byte of cell for a 1-byte payload */ +#ifdef SQLITE_HAS_CODEC + u8 optimalReserve; /* Desired amount of reserved space per page */ +#endif u16 btsFlags; /* Boolean parameters. See BTS_* macros below */ u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */ u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */ @@ -52474,6 +52886,7 @@ static void SQLITE_NOINLINE btreeLockCarefully(Btree *p){ ** Exit the recursive mutex on a Btree. */ SQLITE_PRIVATE void sqlite3BtreeLeave(Btree *p){ + assert( sqlite3_mutex_held(p->db->mutex) ); if( p->sharable ){ assert( p->wantToLock>0 ); p->wantToLock--; @@ -52721,7 +53134,7 @@ static BtShared *SQLITE_WSD sqlite3SharedCacheList = 0; ** The shared cache setting effects only future calls to ** sqlite3_open(), sqlite3_open16(), or sqlite3_open_v2(). */ -SQLITE_API int sqlite3_enable_shared_cache(int enable){ +SQLITE_API int SQLITE_STDCALL sqlite3_enable_shared_cache(int enable){ sqlite3GlobalConfig.sharedCacheEnabled = enable; return SQLITE_OK; } @@ -52810,6 +53223,12 @@ static int hasSharedCacheTableLock( for(p=sqliteHashFirst(&pSchema->idxHash); p; p=sqliteHashNext(p)){ Index *pIdx = (Index *)sqliteHashData(p); if( pIdx->tnum==(int)iRoot ){ + if( iTab ){ + /* Two or more indexes share the same root page. There must + ** be imposter tables. So just return true. The assert is not + ** useful in that case. */ + return 1; + } iTab = pIdx->pTable->tnum; } } @@ -53229,10 +53648,15 @@ static void btreeReleaseAllCursorPages(BtCursor *pCur){ static int saveCursorPosition(BtCursor *pCur){ int rc; - assert( CURSOR_VALID==pCur->eState ); + assert( CURSOR_VALID==pCur->eState || CURSOR_SKIPNEXT==pCur->eState ); assert( 0==pCur->pKey ); assert( cursorHoldsMutex(pCur) ); + if( pCur->eState==CURSOR_SKIPNEXT ){ + pCur->eState = CURSOR_VALID; + }else{ + pCur->skipNext = 0; + } rc = sqlite3BtreeKeySize(pCur, &pCur->nKey); assert( rc==SQLITE_OK ); /* KeySize() cannot fail */ @@ -53303,7 +53727,7 @@ static int SQLITE_NOINLINE saveCursorsOnList( ){ do{ if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) ){ - if( p->eState==CURSOR_VALID ){ + if( p->eState==CURSOR_VALID || p->eState==CURSOR_SKIPNEXT ){ int rc = saveCursorPosition(p); if( SQLITE_OK!=rc ){ return rc; @@ -53375,17 +53799,19 @@ static int btreeMoveto( */ static int btreeRestoreCursorPosition(BtCursor *pCur){ int rc; + int skipNext; assert( cursorHoldsMutex(pCur) ); assert( pCur->eState>=CURSOR_REQUIRESEEK ); if( pCur->eState==CURSOR_FAULT ){ return pCur->skipNext; } pCur->eState = CURSOR_INVALID; - rc = btreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &pCur->skipNext); + rc = btreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &skipNext); if( rc==SQLITE_OK ){ sqlite3_free(pCur->pKey); pCur->pKey = 0; assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_INVALID ); + pCur->skipNext |= skipNext; if( pCur->skipNext && pCur->eState==CURSOR_VALID ){ pCur->eState = CURSOR_SKIPNEXT; } @@ -53437,9 +53863,10 @@ SQLITE_PRIVATE int sqlite3BtreeCursorRestore(BtCursor *pCur, int *pDifferentRow) *pDifferentRow = 1; return rc; } - if( pCur->eState!=CURSOR_VALID || NEVER(pCur->skipNext!=0) ){ + if( pCur->eState!=CURSOR_VALID ){ *pDifferentRow = 1; }else{ + assert( pCur->skipNext==0 ); *pDifferentRow = 0; } return SQLITE_OK; @@ -54580,16 +55007,18 @@ SQLITE_PRIVATE int sqlite3BtreeOpen( */ if( isTempDb==0 && (isMemdb==0 || (vfsFlags&SQLITE_OPEN_URI)!=0) ){ if( vfsFlags & SQLITE_OPEN_SHAREDCACHE ){ + int nFilename = sqlite3Strlen30(zFilename)+1; int nFullPathname = pVfs->mxPathname+1; - char *zFullPathname = sqlite3Malloc(nFullPathname); + char *zFullPathname = sqlite3Malloc(MAX(nFullPathname,nFilename)); MUTEX_LOGIC( sqlite3_mutex *mutexShared; ) + p->sharable = 1; if( !zFullPathname ){ sqlite3_free(p); return SQLITE_NOMEM; } if( isMemdb ){ - memcpy(zFullPathname, zFilename, sqlite3Strlen30(zFilename)+1); + memcpy(zFullPathname, zFilename, nFilename); }else{ rc = sqlite3OsFullPathname(pVfs, zFilename, nFullPathname, zFullPathname); @@ -54646,8 +55075,8 @@ SQLITE_PRIVATE int sqlite3BtreeOpen( ** the right size. This is to guard against size changes that result ** when compiling on a different architecture. */ - assert( sizeof(i64)==8 || sizeof(i64)==4 ); - assert( sizeof(u64)==8 || sizeof(u64)==4 ); + assert( sizeof(i64)==8 ); + assert( sizeof(u64)==8 ); assert( sizeof(u32)==4 ); assert( sizeof(u16)==2 ); assert( sizeof(Pgno)==4 ); @@ -55034,6 +55463,9 @@ SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, BtShared *pBt = p->pBt; assert( nReserve>=-1 && nReserve<=255 ); sqlite3BtreeEnter(p); +#if SQLITE_HAS_CODEC + if( nReserve>pBt->optimalReserve ) pBt->optimalReserve = (u8)nReserve; +#endif if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){ sqlite3BtreeLeave(p); return SQLITE_READONLY; @@ -55045,7 +55477,7 @@ SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE && ((pageSize-1)&pageSize)==0 ){ assert( (pageSize & 7)==0 ); - assert( !pBt->pPage1 && !pBt->pCursor ); + assert( !pBt->pCursor ); pBt->pageSize = (u32)pageSize; freeTempSpace(pBt); } @@ -55063,7 +55495,6 @@ SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree *p){ return p->pBt->pageSize; } -#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_DEBUG) /* ** This function is similar to sqlite3BtreeGetReserve(), except that it ** may only be called if it is guaranteed that the b-tree mutex is already @@ -55076,25 +55507,33 @@ SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree *p){ ** database handle that owns *p, causing undefined behavior. */ SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p){ + int n; assert( sqlite3_mutex_held(p->pBt->mutex) ); - return p->pBt->pageSize - p->pBt->usableSize; + n = p->pBt->pageSize - p->pBt->usableSize; + return n; } -#endif /* SQLITE_HAS_CODEC || SQLITE_DEBUG */ -#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM) /* ** Return the number of bytes of space at the end of every page that ** are intentually left unused. This is the "reserved" space that is ** sometimes used by extensions. +** +** If SQLITE_HAS_MUTEX is defined then the number returned is the +** greater of the current reserved space and the maximum requested +** reserve space. */ -SQLITE_PRIVATE int sqlite3BtreeGetReserve(Btree *p){ +SQLITE_PRIVATE int sqlite3BtreeGetOptimalReserve(Btree *p){ int n; sqlite3BtreeEnter(p); - n = p->pBt->pageSize - p->pBt->usableSize; + n = sqlite3BtreeGetReserveNoMutex(p); +#ifdef SQLITE_HAS_CODEC + if( npBt->optimalReserve ) n = p->pBt->optimalReserve; +#endif sqlite3BtreeLeave(p); return n; } + /* ** Set the maximum page count for a database if mxPage is positive. ** No changes are made if mxPage is 0 or negative. @@ -55125,7 +55564,6 @@ SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree *p, int newFlag){ sqlite3BtreeLeave(p); return b; } -#endif /* !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM) */ /* ** Change the 'auto-vacuum' property of the database. If the 'autoVacuum' @@ -56245,7 +56683,7 @@ SQLITE_PRIVATE int sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode, int wr for(p=pBtree->pBt->pCursor; p; p=p->pNext){ int i; if( writeOnly && (p->curFlags & BTCF_WriteFlag)==0 ){ - if( p->eState==CURSOR_VALID ){ + if( p->eState==CURSOR_VALID || p->eState==CURSOR_SKIPNEXT ){ rc = saveCursorPosition(p); if( rc!=SQLITE_OK ){ (void)sqlite3BtreeTripAllCursors(pBtree, rc, 0); @@ -56651,6 +57089,8 @@ SQLITE_PRIVATE int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){ SQLITE_PRIVATE int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){ assert( cursorHoldsMutex(pCur) ); assert( pCur->eState==CURSOR_VALID ); + assert( pCur->iPage>=0 ); + assert( pCur->iPageapPage[pCur->iPage]->intKeyLeaf==1 ); getCellInfo(pCur); *pSize = pCur->info.nPayload; @@ -57059,13 +57499,18 @@ static const void *fetchPayload( BtCursor *pCur, /* Cursor pointing to entry to read from */ u32 *pAmt /* Write the number of available bytes here */ ){ + u32 amt; assert( pCur!=0 && pCur->iPage>=0 && pCur->apPage[pCur->iPage]); assert( pCur->eState==CURSOR_VALID ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); assert( cursorHoldsMutex(pCur) ); assert( pCur->aiIdx[pCur->iPage]apPage[pCur->iPage]->nCell ); assert( pCur->info.nSize>0 ); - *pAmt = pCur->info.nLocal; + assert( pCur->info.pPayload>pCur->apPage[pCur->iPage]->aData || CORRUPT_DB ); + assert( pCur->info.pPayloadapPage[pCur->iPage]->aDataEnd ||CORRUPT_DB); + amt = (int)(pCur->apPage[pCur->iPage]->aDataEnd - pCur->info.pPayload); + if( pCur->info.nLocalinfo.nLocal; + *pAmt = amt; return (void*)pCur->info.pPayload; } @@ -57129,7 +57574,7 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){ return SQLITE_OK; } -#if 0 +#if SQLITE_DEBUG /* ** Page pParent is an internal (non-leaf) tree page. This function ** asserts that page number iChild is the left-child if the iIdx'th @@ -57138,6 +57583,8 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){ ** the page. */ static void assertParentIndex(MemPage *pParent, int iIdx, Pgno iChild){ + if( CORRUPT_DB ) return; /* The conditions tested below might not be true + ** in a corrupt database */ assert( iIdx<=pParent->nCell ); if( iIdx==pParent->nCell ){ assert( get4byte(&pParent->aData[pParent->hdrOffset+8])==iChild ); @@ -57162,19 +57609,11 @@ static void moveToParent(BtCursor *pCur){ assert( pCur->eState==CURSOR_VALID ); assert( pCur->iPage>0 ); assert( pCur->apPage[pCur->iPage] ); - - /* UPDATE: It is actually possible for the condition tested by the assert - ** below to be untrue if the database file is corrupt. This can occur if - ** one cursor has modified page pParent while a reference to it is held - ** by a second cursor. Which can only happen if a single page is linked - ** into more than one b-tree structure in a corrupt database. */ -#if 0 assertParentIndex( pCur->apPage[pCur->iPage-1], pCur->aiIdx[pCur->iPage-1], pCur->apPage[pCur->iPage]->pgno ); -#endif testcase( pCur->aiIdx[pCur->iPage-1] > pCur->apPage[pCur->iPage-1]->nCell ); releasePage(pCur->apPage[pCur->iPage]); @@ -59349,7 +59788,6 @@ static int balance_nonroot( }else if( iParentIdx==i ){ nxDiv = i-2+bBulk; }else{ - assert( bBulk==0 ); nxDiv = iParentIdx-1; } i = 2-bBulk; @@ -60100,7 +60538,8 @@ static int balance(BtCursor *pCur){ ** pSpace buffer passed to the latter call to balance_nonroot(). */ u8 *pSpace = sqlite3PageMalloc(pCur->pBt->pageSize); - rc = balance_nonroot(pParent, iIdx, pSpace, iPage==1, pCur->hints); + rc = balance_nonroot(pParent, iIdx, pSpace, iPage==1, + pCur->hints&BTREE_BULKLOAD); if( pFree ){ /* If pFree is not NULL, it points to the pSpace buffer used ** by a previous call to balance_nonroot(). Its contents are @@ -60121,6 +60560,7 @@ static int balance(BtCursor *pCur){ /* The next iteration of the do-loop balances the parent page. */ releasePage(pPage); pCur->iPage--; + assert( pCur->iPage>=0 ); } }while( rc==SQLITE_OK ); @@ -60597,9 +61037,13 @@ static int clearDatabasePage( if( pgno>btreePagecount(pBt) ){ return SQLITE_CORRUPT_BKPT; } - rc = getAndInitPage(pBt, pgno, &pPage, 0); if( rc ) return rc; + if( pPage->bBusy ){ + rc = SQLITE_CORRUPT_BKPT; + goto cleardatabasepage_out; + } + pPage->bBusy = 1; hdr = pPage->hdrOffset; for(i=0; inCell; i++){ pCell = findCell(pPage, i); @@ -60624,6 +61068,7 @@ static int clearDatabasePage( } cleardatabasepage_out: + pPage->bBusy = 0; releasePage(pPage); return rc; } @@ -61131,6 +61576,57 @@ static void checkList( } #endif /* SQLITE_OMIT_INTEGRITY_CHECK */ +/* +** An implementation of a min-heap. +** +** aHeap[0] is the number of elements on the heap. aHeap[1] is the +** root element. The daughter nodes of aHeap[N] are aHeap[N*2] +** and aHeap[N*2+1]. +** +** The heap property is this: Every node is less than or equal to both +** of its daughter nodes. A consequence of the heap property is that the +** root node aHeap[1] is always the minimum value currently in the heap. +** +** The btreeHeapInsert() routine inserts an unsigned 32-bit number onto +** the heap, preserving the heap property. The btreeHeapPull() routine +** removes the root element from the heap (the minimum value in the heap) +** and then moves other nodes around as necessary to preserve the heap +** property. +** +** This heap is used for cell overlap and coverage testing. Each u32 +** entry represents the span of a cell or freeblock on a btree page. +** The upper 16 bits are the index of the first byte of a range and the +** lower 16 bits are the index of the last byte of that range. +*/ +static void btreeHeapInsert(u32 *aHeap, u32 x){ + u32 j, i = ++aHeap[0]; + aHeap[i] = x; + while( (j = i/2)>0 && aHeap[j]>aHeap[i] ){ + x = aHeap[j]; + aHeap[j] = aHeap[i]; + aHeap[i] = x; + i = j; + } +} +static int btreeHeapPull(u32 *aHeap, u32 *pOut){ + u32 j, i, x; + if( (x = aHeap[0])==0 ) return 0; + *pOut = aHeap[1]; + aHeap[1] = aHeap[x]; + aHeap[x] = 0xffffffff; + aHeap[0]--; + i = 1; + while( (j = i*2)<=aHeap[0] ){ + if( aHeap[j]>aHeap[j+1] ) j++; + if( aHeap[i]zPfx; @@ -61308,15 +61805,15 @@ static int checkTreePage( */ data = pPage->aData; hdr = pPage->hdrOffset; - hit = sqlite3PageMalloc( pBt->pageSize ); + heap = (u32*)sqlite3PageMalloc( pBt->pageSize ); pCheck->zPfx = 0; - if( hit==0 ){ + if( heap==0 ){ pCheck->mallocFailed = 1; }else{ int contentOffset = get2byteNotZero(&data[hdr+5]); assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */ - memset(hit+contentOffset, 0, usableSize-contentOffset); - memset(hit, 1, contentOffset); + heap[0] = 0; + btreeHeapInsert(heap, contentOffset-1); /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the ** number of cells on the page. */ nCell = get2byte(&data[hdr+3]); @@ -61328,7 +61825,6 @@ static int checkTreePage( for(i=0; i=pc; j--) hit[j]++; + btreeHeapInsert(heap, (pc<<16)|(pc+size-1)); } } /* EVIDENCE-OF: R-20690-50594 The second field of the b-tree page header @@ -61349,7 +61845,7 @@ static int checkTreePage( assert( i<=usableSize-4 ); /* Enforced by btreeInitPage() */ size = get2byte(&data[i+2]); assert( i+size<=usableSize ); /* Enforced by btreeInitPage() */ - for(j=i+size-1; j>=i; j--) hit[j]++; + btreeHeapInsert(heap, (i<<16)|(i+size-1)); /* EVIDENCE-OF: R-58208-19414 The first 2 bytes of a freeblock are a ** big-endian integer which is the offset in the b-tree page of the next ** freeblock in the chain, or zero if the freeblock is the last on the @@ -61361,27 +61857,33 @@ static int checkTreePage( assert( j<=usableSize-4 ); /* Enforced by btreeInitPage() */ i = j; } - for(i=cnt=0; i1 ){ + cnt = 0; + assert( heap[0]>0 ); + assert( (heap[1]>>16)==0 ); + btreeHeapPull(heap,&prev); + while( btreeHeapPull(heap,&x) ){ + if( (prev&0xffff)+1>(x>>16) ){ checkAppendMsg(pCheck, - "Multiple uses for byte %d of page %d", i, iPage); + "Multiple uses for byte %u of page %d", x>>16, iPage); break; + }else{ + cnt += (x>>16) - (prev&0xffff) - 1; + prev = x; } } + cnt += usableSize - (prev&0xffff) - 1; /* EVIDENCE-OF: R-43263-13491 The total number of bytes in all fragments ** is stored in the fifth field of the b-tree page header. ** EVIDENCE-OF: R-07161-27322 The one-byte integer at offset 7 gives the ** number of fragmented free bytes within the cell content area. */ - if( cnt!=data[hdr+7] ){ + if( heap[0]==0 && cnt!=data[hdr+7] ){ checkAppendMsg(pCheck, "Fragmentation of %d bytes reported as %d on page %d", cnt, data[hdr+7], iPage); } } - sqlite3PageFree(hit); + sqlite3PageFree(heap); releasePage(pPage); end_of_check: @@ -61445,8 +61947,7 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( } i = PENDING_BYTE_PAGE(pBt); if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i); - sqlite3StrAccumInit(&sCheck.errMsg, zErr, sizeof(zErr), SQLITE_MAX_LENGTH); - sCheck.errMsg.useMalloc = 2; + sqlite3StrAccumInit(&sCheck.errMsg, 0, zErr, sizeof(zErr), SQLITE_MAX_LENGTH); /* Check the integrity of the freelist */ @@ -61763,14 +62264,23 @@ SQLITE_PRIVATE int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){ } /* -** set the mask of hint flags for cursor pCsr. Currently the only valid -** values are 0 and BTREE_BULKLOAD. +** set the mask of hint flags for cursor pCsr. */ SQLITE_PRIVATE void sqlite3BtreeCursorHints(BtCursor *pCsr, unsigned int mask){ - assert( mask==BTREE_BULKLOAD || mask==0 ); + assert( mask==BTREE_BULKLOAD || mask==BTREE_SEEK_EQ || mask==0 ); pCsr->hints = mask; } +#ifdef SQLITE_DEBUG +/* +** Return true if the cursor has a hint specified. This routine is +** only used from within assert() statements +*/ +SQLITE_PRIVATE int sqlite3BtreeCursorHasHint(BtCursor *pCsr, unsigned int mask){ + return (pCsr->hints & mask)!=0; +} +#endif + /* ** Return true if the given Btree is read-only. */ @@ -61929,7 +62439,7 @@ static int checkReadTransaction(sqlite3 *db, Btree *p){ ** If an error occurs, NULL is returned and an error code and error message ** stored in database handle pDestDb. */ -SQLITE_API sqlite3_backup *sqlite3_backup_init( +SQLITE_API sqlite3_backup *SQLITE_STDCALL sqlite3_backup_init( sqlite3* pDestDb, /* Database to write to */ const char *zDestDb, /* Name of database within pDestDb */ sqlite3* pSrcDb, /* Database connection to read from */ @@ -62032,7 +62542,7 @@ static int backupOnePage( ** guaranteed that the shared-mutex is held by this thread, handle ** p->pSrc may not actually be the owner. */ int nSrcReserve = sqlite3BtreeGetReserveNoMutex(p->pSrc); - int nDestReserve = sqlite3BtreeGetReserve(p->pDest); + int nDestReserve = sqlite3BtreeGetOptimalReserve(p->pDest); #endif int rc = SQLITE_OK; i64 iOff; @@ -62137,7 +62647,7 @@ static void attachBackupObject(sqlite3_backup *p){ /* ** Copy nPage pages from the source b-tree to the destination. */ -SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){ +SQLITE_API int SQLITE_STDCALL sqlite3_backup_step(sqlite3_backup *p, int nPage){ int rc; int destMode; /* Destination journal mode */ int pgszSrc = 0; /* Source page size */ @@ -62382,7 +62892,7 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){ /* ** Release all resources associated with an sqlite3_backup* handle. */ -SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p){ +SQLITE_API int SQLITE_STDCALL sqlite3_backup_finish(sqlite3_backup *p){ sqlite3_backup **pp; /* Ptr to head of pagers backup list */ sqlite3 *pSrcDb; /* Source database connection */ int rc; /* Value to return */ @@ -62434,7 +62944,7 @@ SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p){ ** Return the number of pages still to be backed up as of the most recent ** call to sqlite3_backup_step(). */ -SQLITE_API int sqlite3_backup_remaining(sqlite3_backup *p){ +SQLITE_API int SQLITE_STDCALL sqlite3_backup_remaining(sqlite3_backup *p){ #ifdef SQLITE_ENABLE_API_ARMOR if( p==0 ){ (void)SQLITE_MISUSE_BKPT; @@ -62448,7 +62958,7 @@ SQLITE_API int sqlite3_backup_remaining(sqlite3_backup *p){ ** Return the total number of pages in the source database as of the most ** recent call to sqlite3_backup_step(). */ -SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p){ +SQLITE_API int SQLITE_STDCALL sqlite3_backup_pagecount(sqlite3_backup *p){ #ifdef SQLITE_ENABLE_API_ARMOR if( p==0 ){ (void)SQLITE_MISUSE_BKPT; @@ -62773,10 +63283,11 @@ SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem *pMem){ pMem->z[pMem->n] = 0; pMem->z[pMem->n+1] = 0; pMem->flags |= MEM_Term; -#ifdef SQLITE_DEBUG - pMem->pScopyFrom = 0; -#endif } + pMem->flags &= ~MEM_Ephem; +#ifdef SQLITE_DEBUG + pMem->pScopyFrom = 0; +#endif return SQLITE_OK; } @@ -63663,7 +64174,7 @@ struct ValueNewStat4Ctx { ** Otherwise, if the second argument is non-zero, then this function is ** being called indirectly by sqlite3Stat4ProbeSetValue(). If it has not ** already been allocated, allocate the UnpackedRecord structure that -** that function will return to its caller here. Then return a pointer +** that function will return to its caller here. Then return a pointer to ** an sqlite3_value within the UnpackedRecord.a[] array. */ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){ @@ -63707,6 +64218,113 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){ return sqlite3ValueNew(db); } +/* +** The expression object indicated by the second argument is guaranteed +** to be a scalar SQL function. If +** +** * all function arguments are SQL literals, +** * the SQLITE_FUNC_CONSTANT function flag is set, and +** * the SQLITE_FUNC_NEEDCOLL function flag is not set, +** +** then this routine attempts to invoke the SQL function. Assuming no +** error occurs, output parameter (*ppVal) is set to point to a value +** object containing the result before returning SQLITE_OK. +** +** Affinity aff is applied to the result of the function before returning. +** If the result is a text value, the sqlite3_value object uses encoding +** enc. +** +** If the conditions above are not met, this function returns SQLITE_OK +** and sets (*ppVal) to NULL. Or, if an error occurs, (*ppVal) is set to +** NULL and an SQLite error code returned. +*/ +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 +static int valueFromFunction( + sqlite3 *db, /* The database connection */ + Expr *p, /* The expression to evaluate */ + u8 enc, /* Encoding to use */ + u8 aff, /* Affinity to use */ + sqlite3_value **ppVal, /* Write the new value here */ + struct ValueNewStat4Ctx *pCtx /* Second argument for valueNew() */ +){ + sqlite3_context ctx; /* Context object for function invocation */ + sqlite3_value **apVal = 0; /* Function arguments */ + int nVal = 0; /* Size of apVal[] array */ + FuncDef *pFunc = 0; /* Function definition */ + sqlite3_value *pVal = 0; /* New value */ + int rc = SQLITE_OK; /* Return code */ + int nName; /* Size of function name in bytes */ + ExprList *pList = 0; /* Function arguments */ + int i; /* Iterator variable */ + + assert( pCtx!=0 ); + assert( (p->flags & EP_TokenOnly)==0 ); + pList = p->x.pList; + if( pList ) nVal = pList->nExpr; + nName = sqlite3Strlen30(p->u.zToken); + pFunc = sqlite3FindFunction(db, p->u.zToken, nName, nVal, enc, 0); + assert( pFunc ); + if( (pFunc->funcFlags & SQLITE_FUNC_CONSTANT)==0 + || (pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL) + ){ + return SQLITE_OK; + } + + if( pList ){ + apVal = (sqlite3_value**)sqlite3DbMallocZero(db, sizeof(apVal[0]) * nVal); + if( apVal==0 ){ + rc = SQLITE_NOMEM; + goto value_from_function_out; + } + for(i=0; ia[i].pExpr, enc, aff, &apVal[i]); + if( apVal[i]==0 || rc!=SQLITE_OK ) goto value_from_function_out; + } + } + + pVal = valueNew(db, pCtx); + if( pVal==0 ){ + rc = SQLITE_NOMEM; + goto value_from_function_out; + } + + assert( pCtx->pParse->rc==SQLITE_OK ); + memset(&ctx, 0, sizeof(ctx)); + ctx.pOut = pVal; + ctx.pFunc = pFunc; + pFunc->xFunc(&ctx, nVal, apVal); + if( ctx.isError ){ + rc = ctx.isError; + sqlite3ErrorMsg(pCtx->pParse, "%s", sqlite3_value_text(pVal)); + }else{ + sqlite3ValueApplyAffinity(pVal, aff, SQLITE_UTF8); + assert( rc==SQLITE_OK ); + rc = sqlite3VdbeChangeEncoding(pVal, enc); + if( rc==SQLITE_OK && sqlite3VdbeMemTooBig(pVal) ){ + rc = SQLITE_TOOBIG; + pCtx->pParse->nErr++; + } + } + pCtx->pParse->rc = rc; + + value_from_function_out: + if( rc!=SQLITE_OK ){ + pVal = 0; + } + if( apVal ){ + for(i=0; iop)==TK_UPLUS ) pExpr = pExpr->pLeft; if( NEVER(op==TK_REGISTER) ) op = pExpr->op2; + /* Compressed expressions only appear when parsing the DEFAULT clause + ** on a table column definition, and hence only when pCtx==0. This + ** check ensures that an EP_TokenOnly expression is never passed down + ** into valueFromFunction(). */ + assert( (pExpr->flags & EP_TokenOnly)==0 || pCtx==0 ); + if( op==TK_CAST ){ u8 aff = sqlite3AffinityType(pExpr->u.zToken,0); rc = valueFromExpr(db, pExpr->pLeft, enc, aff, ppVal, pCtx); @@ -63815,6 +64439,12 @@ static int valueFromExpr( } #endif +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + else if( op==TK_FUNCTION && pCtx!=0 ){ + rc = valueFromFunction(db, pExpr, enc, affinity, &pVal, pCtx); + } +#endif + *ppVal = pVal; return rc; @@ -64101,7 +64731,7 @@ SQLITE_PRIVATE void sqlite3Stat4ProbeFree(UnpackedRecord *pRec){ Mem *aMem = pRec->aMem; sqlite3 *db = aMem[0].db; for(i=0; ipKeyInfo); sqlite3DbFree(db, pRec); @@ -64204,7 +64834,7 @@ SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, int isPrepa /* ** Return the SQL associated with a prepared statement */ -SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt){ +SQLITE_API const char *SQLITE_STDCALL sqlite3_sql(sqlite3_stmt *pStmt){ Vdbe *p = (Vdbe *)pStmt; return (p && p->isPrepareV2) ? p->zSql : 0; } @@ -65267,7 +65897,7 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){ #ifndef SQLITE_OMIT_VIRTUALTABLE case P4_VTAB: { sqlite3_vtab *pVtab = pOp->p4.pVtab->pVtab; - sqlite3_snprintf(nTemp, zTemp, "vtab:%p:%p", pVtab, pVtab->pModule); + sqlite3_snprintf(nTemp, zTemp, "vtab:%p", pVtab); break; } #endif @@ -65931,13 +66561,29 @@ SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){ else if( pCx->pVtabCursor ){ sqlite3_vtab_cursor *pVtabCursor = pCx->pVtabCursor; const sqlite3_module *pModule = pVtabCursor->pVtab->pModule; - p->inVtabMethod = 1; + assert( pVtabCursor->pVtab->nRef>0 ); + pVtabCursor->pVtab->nRef--; pModule->xClose(pVtabCursor); - p->inVtabMethod = 0; } #endif } +/* +** Close all cursors in the current frame. +*/ +static void closeCursorsInFrame(Vdbe *p){ + if( p->apCsr ){ + int i; + for(i=0; inCursor; i++){ + VdbeCursor *pC = p->apCsr[i]; + if( pC ){ + sqlite3VdbeFreeCursor(p, pC); + p->apCsr[i] = 0; + } + } + } +} + /* ** Copy the values stored in the VdbeFrame structure to its Vdbe. This ** is used, for example, when a trigger sub-program is halted to restore @@ -65945,6 +66591,7 @@ SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){ */ SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){ Vdbe *v = pFrame->v; + closeCursorsInFrame(v); #ifdef SQLITE_ENABLE_STMT_SCANSTATUS v->anExec = pFrame->anExec; #endif @@ -65979,17 +66626,7 @@ static void closeAllCursors(Vdbe *p){ p->nFrame = 0; } assert( p->nFrame==0 ); - - if( p->apCsr ){ - int i; - for(i=0; inCursor; i++){ - VdbeCursor *pC = p->apCsr[i]; - if( pC ){ - sqlite3VdbeFreeCursor(p, pC); - p->apCsr[i] = 0; - } - } - } + closeCursorsInFrame(p); if( p->aMem ){ releaseMemArray(&p->aMem[1], p->nMem); } @@ -66292,7 +66929,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ ** doing this the directory is synced again before any individual ** transaction files are deleted. */ - rc = sqlite3OsDelete(pVfs, zMaster, 1); + rc = sqlite3OsDelete(pVfs, zMaster, needSync); sqlite3DbFree(db, zMaster); zMaster = 0; if( rc ){ @@ -67522,7 +68159,8 @@ static void vdbeAssertFieldCountWithinLimits( if( CORRUPT_DB ) return; idx = getVarint32(aKey, szHdr); - assert( szHdr<=nKey ); + assert( nKey>=0 ); + assert( szHdr<=(u32)nKey ); while( idxerrCode is set to SQLITE_NOMEM and, if it is not NULL, the ** malloc-failed flag set on database handle (pPKey2->pKeyInfo->db). */ -static int vdbeRecordCompareWithSkip( +SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip( int nKey1, const void *pKey1, /* Left key */ UnpackedRecord *pPKey2, /* Right key */ int bSkip /* If true, skip the first field */ @@ -67919,7 +68557,7 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompare( int nKey1, const void *pKey1, /* Left key */ UnpackedRecord *pPKey2 /* Right key */ ){ - return vdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 0); + return sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 0); } @@ -68007,7 +68645,7 @@ static int vdbeRecordCompareInt( }else if( pPKey2->nField>1 ){ /* The first fields of the two keys are equal. Compare the trailing ** fields. */ - res = vdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1); + res = sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1); }else{ /* The first fields of the two keys are equal and there are no trailing ** fields. Return pPKey2->default_rc in this case. */ @@ -68055,7 +68693,7 @@ static int vdbeRecordCompareString( res = nStr - pPKey2->aMem[0].n; if( res==0 ){ if( pPKey2->nField>1 ){ - res = vdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1); + res = sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1); }else{ res = pPKey2->default_rc; } @@ -68359,7 +68997,7 @@ SQLITE_PRIVATE void sqlite3VtabImportErrmsg(Vdbe *p, sqlite3_vtab *pVtab){ ** collating sequences are registered or if an authorizer function is ** added or changed. */ -SQLITE_API int sqlite3_expired(sqlite3_stmt *pStmt){ +SQLITE_API int SQLITE_STDCALL sqlite3_expired(sqlite3_stmt *pStmt){ Vdbe *p = (Vdbe*)pStmt; return p==0 || p->expired; } @@ -68396,7 +69034,7 @@ static int vdbeSafetyNotNull(Vdbe *p){ ** This routine sets the error code and string returned by ** sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16(). */ -SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt){ +SQLITE_API int SQLITE_STDCALL sqlite3_finalize(sqlite3_stmt *pStmt){ int rc; if( pStmt==0 ){ /* IMPLEMENTATION-OF: R-57228-12904 Invoking sqlite3_finalize() on a NULL @@ -68422,7 +69060,7 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt){ ** This routine sets the error code and string returned by ** sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16(). */ -SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt){ +SQLITE_API int SQLITE_STDCALL sqlite3_reset(sqlite3_stmt *pStmt){ int rc; if( pStmt==0 ){ rc = SQLITE_OK; @@ -68441,7 +69079,7 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt){ /* ** Set all the parameters in the compiled SQL statement to NULL. */ -SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt *pStmt){ +SQLITE_API int SQLITE_STDCALL sqlite3_clear_bindings(sqlite3_stmt *pStmt){ int i; int rc = SQLITE_OK; Vdbe *p = (Vdbe*)pStmt; @@ -68465,7 +69103,7 @@ SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt *pStmt){ ** The following routines extract information from a Mem or sqlite3_value ** structure. */ -SQLITE_API const void *sqlite3_value_blob(sqlite3_value *pVal){ +SQLITE_API const void *SQLITE_STDCALL sqlite3_value_blob(sqlite3_value *pVal){ Mem *p = (Mem*)pVal; if( p->flags & (MEM_Blob|MEM_Str) ){ sqlite3VdbeMemExpandBlob(p); @@ -68475,36 +69113,40 @@ SQLITE_API const void *sqlite3_value_blob(sqlite3_value *pVal){ return sqlite3_value_text(pVal); } } -SQLITE_API int sqlite3_value_bytes(sqlite3_value *pVal){ +SQLITE_API int SQLITE_STDCALL sqlite3_value_bytes(sqlite3_value *pVal){ return sqlite3ValueBytes(pVal, SQLITE_UTF8); } -SQLITE_API int sqlite3_value_bytes16(sqlite3_value *pVal){ +SQLITE_API int SQLITE_STDCALL sqlite3_value_bytes16(sqlite3_value *pVal){ return sqlite3ValueBytes(pVal, SQLITE_UTF16NATIVE); } -SQLITE_API double sqlite3_value_double(sqlite3_value *pVal){ +SQLITE_API double SQLITE_STDCALL sqlite3_value_double(sqlite3_value *pVal){ return sqlite3VdbeRealValue((Mem*)pVal); } -SQLITE_API int sqlite3_value_int(sqlite3_value *pVal){ +SQLITE_API int SQLITE_STDCALL sqlite3_value_int(sqlite3_value *pVal){ return (int)sqlite3VdbeIntValue((Mem*)pVal); } -SQLITE_API sqlite_int64 sqlite3_value_int64(sqlite3_value *pVal){ +SQLITE_API sqlite_int64 SQLITE_STDCALL sqlite3_value_int64(sqlite3_value *pVal){ return sqlite3VdbeIntValue((Mem*)pVal); } -SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value *pVal){ +SQLITE_API const unsigned char *SQLITE_STDCALL sqlite3_value_text(sqlite3_value *pVal){ return (const unsigned char *)sqlite3ValueText(pVal, SQLITE_UTF8); } #ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlite3_value_text16(sqlite3_value* pVal){ +SQLITE_API const void *SQLITE_STDCALL sqlite3_value_text16(sqlite3_value* pVal){ return sqlite3ValueText(pVal, SQLITE_UTF16NATIVE); } -SQLITE_API const void *sqlite3_value_text16be(sqlite3_value *pVal){ +SQLITE_API const void *SQLITE_STDCALL sqlite3_value_text16be(sqlite3_value *pVal){ return sqlite3ValueText(pVal, SQLITE_UTF16BE); } -SQLITE_API const void *sqlite3_value_text16le(sqlite3_value *pVal){ +SQLITE_API const void *SQLITE_STDCALL sqlite3_value_text16le(sqlite3_value *pVal){ return sqlite3ValueText(pVal, SQLITE_UTF16LE); } #endif /* SQLITE_OMIT_UTF16 */ -SQLITE_API int sqlite3_value_type(sqlite3_value* pVal){ +/* EVIDENCE-OF: R-12793-43283 Every value in SQLite has one of five +** fundamental datatypes: 64-bit signed integer 64-bit IEEE floating +** point number string BLOB NULL +*/ +SQLITE_API int SQLITE_STDCALL sqlite3_value_type(sqlite3_value* pVal){ static const u8 aType[] = { SQLITE_BLOB, /* 0x00 */ SQLITE_NULL, /* 0x01 */ @@ -68580,7 +69222,7 @@ static int invokeValueDestructor( if( pCtx ) sqlite3_result_error_toobig(pCtx); return SQLITE_TOOBIG; } -SQLITE_API void sqlite3_result_blob( +SQLITE_API void SQLITE_STDCALL sqlite3_result_blob( sqlite3_context *pCtx, const void *z, int n, @@ -68590,7 +69232,7 @@ SQLITE_API void sqlite3_result_blob( assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); setResultStrOrError(pCtx, z, n, 0, xDel); } -SQLITE_API void sqlite3_result_blob64( +SQLITE_API void SQLITE_STDCALL sqlite3_result_blob64( sqlite3_context *pCtx, const void *z, sqlite3_uint64 n, @@ -68604,37 +69246,37 @@ SQLITE_API void sqlite3_result_blob64( setResultStrOrError(pCtx, z, (int)n, 0, xDel); } } -SQLITE_API void sqlite3_result_double(sqlite3_context *pCtx, double rVal){ +SQLITE_API void SQLITE_STDCALL sqlite3_result_double(sqlite3_context *pCtx, double rVal){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetDouble(pCtx->pOut, rVal); } -SQLITE_API void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){ +SQLITE_API void SQLITE_STDCALL sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); pCtx->isError = SQLITE_ERROR; pCtx->fErrorOrAux = 1; sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF8, SQLITE_TRANSIENT); } #ifndef SQLITE_OMIT_UTF16 -SQLITE_API void sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int n){ +SQLITE_API void SQLITE_STDCALL sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int n){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); pCtx->isError = SQLITE_ERROR; pCtx->fErrorOrAux = 1; sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT); } #endif -SQLITE_API void sqlite3_result_int(sqlite3_context *pCtx, int iVal){ +SQLITE_API void SQLITE_STDCALL sqlite3_result_int(sqlite3_context *pCtx, int iVal){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetInt64(pCtx->pOut, (i64)iVal); } -SQLITE_API void sqlite3_result_int64(sqlite3_context *pCtx, i64 iVal){ +SQLITE_API void SQLITE_STDCALL sqlite3_result_int64(sqlite3_context *pCtx, i64 iVal){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetInt64(pCtx->pOut, iVal); } -SQLITE_API void sqlite3_result_null(sqlite3_context *pCtx){ +SQLITE_API void SQLITE_STDCALL sqlite3_result_null(sqlite3_context *pCtx){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetNull(pCtx->pOut); } -SQLITE_API void sqlite3_result_text( +SQLITE_API void SQLITE_STDCALL sqlite3_result_text( sqlite3_context *pCtx, const char *z, int n, @@ -68643,7 +69285,7 @@ SQLITE_API void sqlite3_result_text( assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); setResultStrOrError(pCtx, z, n, SQLITE_UTF8, xDel); } -SQLITE_API void sqlite3_result_text64( +SQLITE_API void SQLITE_STDCALL sqlite3_result_text64( sqlite3_context *pCtx, const char *z, sqlite3_uint64 n, @@ -68660,7 +69302,7 @@ SQLITE_API void sqlite3_result_text64( } } #ifndef SQLITE_OMIT_UTF16 -SQLITE_API void sqlite3_result_text16( +SQLITE_API void SQLITE_STDCALL sqlite3_result_text16( sqlite3_context *pCtx, const void *z, int n, @@ -68669,7 +69311,7 @@ SQLITE_API void sqlite3_result_text16( assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); setResultStrOrError(pCtx, z, n, SQLITE_UTF16NATIVE, xDel); } -SQLITE_API void sqlite3_result_text16be( +SQLITE_API void SQLITE_STDCALL sqlite3_result_text16be( sqlite3_context *pCtx, const void *z, int n, @@ -68678,7 +69320,7 @@ SQLITE_API void sqlite3_result_text16be( assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); setResultStrOrError(pCtx, z, n, SQLITE_UTF16BE, xDel); } -SQLITE_API void sqlite3_result_text16le( +SQLITE_API void SQLITE_STDCALL sqlite3_result_text16le( sqlite3_context *pCtx, const void *z, int n, @@ -68688,17 +69330,20 @@ SQLITE_API void sqlite3_result_text16le( setResultStrOrError(pCtx, z, n, SQLITE_UTF16LE, xDel); } #endif /* SQLITE_OMIT_UTF16 */ -SQLITE_API void sqlite3_result_value(sqlite3_context *pCtx, sqlite3_value *pValue){ +SQLITE_API void SQLITE_STDCALL sqlite3_result_value(sqlite3_context *pCtx, sqlite3_value *pValue){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemCopy(pCtx->pOut, pValue); } -SQLITE_API void sqlite3_result_zeroblob(sqlite3_context *pCtx, int n){ +SQLITE_API void SQLITE_STDCALL sqlite3_result_zeroblob(sqlite3_context *pCtx, int n){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetZeroBlob(pCtx->pOut, n); } -SQLITE_API void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){ +SQLITE_API void SQLITE_STDCALL sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){ pCtx->isError = errCode; pCtx->fErrorOrAux = 1; +#ifdef SQLITE_DEBUG + if( pCtx->pVdbe ) pCtx->pVdbe->rcApp = errCode; +#endif if( pCtx->pOut->flags & MEM_Null ){ sqlite3VdbeMemSetStr(pCtx->pOut, sqlite3ErrStr(errCode), -1, SQLITE_UTF8, SQLITE_STATIC); @@ -68706,7 +69351,7 @@ SQLITE_API void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){ } /* Force an SQLITE_TOOBIG error. */ -SQLITE_API void sqlite3_result_error_toobig(sqlite3_context *pCtx){ +SQLITE_API void SQLITE_STDCALL sqlite3_result_error_toobig(sqlite3_context *pCtx){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); pCtx->isError = SQLITE_TOOBIG; pCtx->fErrorOrAux = 1; @@ -68715,7 +69360,7 @@ SQLITE_API void sqlite3_result_error_toobig(sqlite3_context *pCtx){ } /* An SQLITE_NOMEM error. */ -SQLITE_API void sqlite3_result_error_nomem(sqlite3_context *pCtx){ +SQLITE_API void SQLITE_STDCALL sqlite3_result_error_nomem(sqlite3_context *pCtx){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetNull(pCtx->pOut); pCtx->isError = SQLITE_NOMEM; @@ -68779,7 +69424,7 @@ static int sqlite3Step(Vdbe *p){ ** or SQLITE_BUSY error. */ #ifdef SQLITE_OMIT_AUTORESET - if( p->rc==SQLITE_BUSY || p->rc==SQLITE_LOCKED ){ + if( (rc = p->rc&0xff)==SQLITE_BUSY || rc==SQLITE_LOCKED ){ sqlite3_reset((sqlite3_stmt*)p); }else{ return SQLITE_MISUSE_BKPT; @@ -68825,6 +69470,9 @@ static int sqlite3Step(Vdbe *p){ if( p->bIsReader ) db->nVdbeRead++; p->pc = 0; } +#ifdef SQLITE_DEBUG + p->rcApp = SQLITE_OK; +#endif #ifndef SQLITE_OMIT_EXPLAIN if( p->explain ){ rc = sqlite3VdbeList(p); @@ -68869,7 +69517,7 @@ end_of_step: assert( rc==SQLITE_ROW || rc==SQLITE_DONE || rc==SQLITE_ERROR || rc==SQLITE_BUSY || rc==SQLITE_MISUSE ); - assert( p->rc!=SQLITE_ROW && p->rc!=SQLITE_DONE ); + assert( (p->rc!=SQLITE_ROW && p->rc!=SQLITE_DONE) || p->rc==p->rcApp ); if( p->isPrepareV2 && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){ /* If this statement was prepared using sqlite3_prepare_v2(), and an ** error has occurred, then return the error code in p->rc to the @@ -68885,7 +69533,7 @@ end_of_step: ** sqlite3Step() to do most of the work. If a schema error occurs, ** call sqlite3Reprepare() and try again. */ -SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){ +SQLITE_API int SQLITE_STDCALL sqlite3_step(sqlite3_stmt *pStmt){ int rc = SQLITE_OK; /* Result from sqlite3Step() */ int rc2 = SQLITE_OK; /* Result from sqlite3Reprepare() */ Vdbe *v = (Vdbe*)pStmt; /* the prepared statement */ @@ -68936,7 +69584,7 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){ ** Extract the user data from a sqlite3_context structure and return a ** pointer to it. */ -SQLITE_API void *sqlite3_user_data(sqlite3_context *p){ +SQLITE_API void *SQLITE_STDCALL sqlite3_user_data(sqlite3_context *p){ assert( p && p->pFunc ); return p->pFunc->pUserData; } @@ -68951,22 +69599,32 @@ SQLITE_API void *sqlite3_user_data(sqlite3_context *p){ ** sqlite3_create_function16() routines that originally registered the ** application defined function. */ -SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){ +SQLITE_API sqlite3 *SQLITE_STDCALL sqlite3_context_db_handle(sqlite3_context *p){ assert( p && p->pFunc ); return p->pOut->db; } /* -** Return the current time for a statement +** Return the current time for a statement. If the current time +** is requested more than once within the same run of a single prepared +** statement, the exact same time is returned for each invocation regardless +** of the amount of time that elapses between invocations. In other words, +** the time returned is always the time of the first call. */ SQLITE_PRIVATE sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context *p){ - Vdbe *v = p->pVdbe; int rc; - if( v->iCurrentTime==0 ){ - rc = sqlite3OsCurrentTimeInt64(p->pOut->db->pVfs, &v->iCurrentTime); - if( rc ) v->iCurrentTime = 0; +#ifndef SQLITE_ENABLE_STAT3_OR_STAT4 + sqlite3_int64 *piTime = &p->pVdbe->iCurrentTime; + assert( p->pVdbe!=0 ); +#else + sqlite3_int64 iTime = 0; + sqlite3_int64 *piTime = p->pVdbe!=0 ? &p->pVdbe->iCurrentTime : &iTime; +#endif + if( *piTime==0 ){ + rc = sqlite3OsCurrentTimeInt64(p->pOut->db->pVfs, piTime); + if( rc ) *piTime = 0; } - return v->iCurrentTime; + return *piTime; } /* @@ -69017,7 +69675,7 @@ static SQLITE_NOINLINE void *createAggContext(sqlite3_context *p, int nByte){ ** context is allocated on the first call. Subsequent calls return the ** same context that was returned on prior calls. */ -SQLITE_API void *sqlite3_aggregate_context(sqlite3_context *p, int nByte){ +SQLITE_API void *SQLITE_STDCALL sqlite3_aggregate_context(sqlite3_context *p, int nByte){ assert( p && p->pFunc && p->pFunc->xStep ); assert( sqlite3_mutex_held(p->pOut->db->mutex) ); testcase( nByte<0 ); @@ -69032,10 +69690,15 @@ SQLITE_API void *sqlite3_aggregate_context(sqlite3_context *p, int nByte){ ** Return the auxiliary data pointer, if any, for the iArg'th argument to ** the user-function defined by pCtx. */ -SQLITE_API void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){ +SQLITE_API void *SQLITE_STDCALL sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){ AuxData *pAuxData; assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); +#if SQLITE_ENABLE_STAT3_OR_STAT4 + if( pCtx->pVdbe==0 ) return 0; +#else + assert( pCtx->pVdbe!=0 ); +#endif for(pAuxData=pCtx->pVdbe->pAuxData; pAuxData; pAuxData=pAuxData->pNext){ if( pAuxData->iOp==pCtx->iOp && pAuxData->iArg==iArg ) break; } @@ -69048,7 +69711,7 @@ SQLITE_API void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){ ** argument to the user-function defined by pCtx. Any previous value is ** deleted by calling the delete function specified when it was set. */ -SQLITE_API void sqlite3_set_auxdata( +SQLITE_API void SQLITE_STDCALL sqlite3_set_auxdata( sqlite3_context *pCtx, int iArg, void *pAux, @@ -69059,6 +69722,11 @@ SQLITE_API void sqlite3_set_auxdata( assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); if( iArg<0 ) goto failed; +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + if( pVdbe==0 ) goto failed; +#else + assert( pVdbe!=0 ); +#endif for(pAuxData=pVdbe->pAuxData; pAuxData; pAuxData=pAuxData->pNext){ if( pAuxData->iOp==pCtx->iOp && pAuxData->iArg==iArg ) break; @@ -69098,7 +69766,7 @@ failed: ** implementations should keep their own counts within their aggregate ** context. */ -SQLITE_API int sqlite3_aggregate_count(sqlite3_context *p){ +SQLITE_API int SQLITE_STDCALL sqlite3_aggregate_count(sqlite3_context *p){ assert( p && p->pMem && p->pFunc && p->pFunc->xStep ); return p->pMem->n; } @@ -69107,7 +69775,7 @@ SQLITE_API int sqlite3_aggregate_count(sqlite3_context *p){ /* ** Return the number of columns in the result set for the statement pStmt. */ -SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt){ +SQLITE_API int SQLITE_STDCALL sqlite3_column_count(sqlite3_stmt *pStmt){ Vdbe *pVm = (Vdbe *)pStmt; return pVm ? pVm->nResColumn : 0; } @@ -69116,7 +69784,7 @@ SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt){ ** Return the number of values available from the current row of the ** currently executing statement pStmt. */ -SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt){ +SQLITE_API int SQLITE_STDCALL sqlite3_data_count(sqlite3_stmt *pStmt){ Vdbe *pVm = (Vdbe *)pStmt; if( pVm==0 || pVm->pResultSet==0 ) return 0; return pVm->nResColumn; @@ -69218,7 +69886,7 @@ static void columnMallocFailure(sqlite3_stmt *pStmt) ** The following routines are used to access elements of the current row ** in the result set. */ -SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt *pStmt, int i){ +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_blob(sqlite3_stmt *pStmt, int i){ const void *val; val = sqlite3_value_blob( columnMem(pStmt,i) ); /* Even though there is no encoding conversion, value_blob() might @@ -69228,37 +69896,37 @@ SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt *pStmt, int i){ columnMallocFailure(pStmt); return val; } -SQLITE_API int sqlite3_column_bytes(sqlite3_stmt *pStmt, int i){ +SQLITE_API int SQLITE_STDCALL sqlite3_column_bytes(sqlite3_stmt *pStmt, int i){ int val = sqlite3_value_bytes( columnMem(pStmt,i) ); columnMallocFailure(pStmt); return val; } -SQLITE_API int sqlite3_column_bytes16(sqlite3_stmt *pStmt, int i){ +SQLITE_API int SQLITE_STDCALL sqlite3_column_bytes16(sqlite3_stmt *pStmt, int i){ int val = sqlite3_value_bytes16( columnMem(pStmt,i) ); columnMallocFailure(pStmt); return val; } -SQLITE_API double sqlite3_column_double(sqlite3_stmt *pStmt, int i){ +SQLITE_API double SQLITE_STDCALL sqlite3_column_double(sqlite3_stmt *pStmt, int i){ double val = sqlite3_value_double( columnMem(pStmt,i) ); columnMallocFailure(pStmt); return val; } -SQLITE_API int sqlite3_column_int(sqlite3_stmt *pStmt, int i){ +SQLITE_API int SQLITE_STDCALL sqlite3_column_int(sqlite3_stmt *pStmt, int i){ int val = sqlite3_value_int( columnMem(pStmt,i) ); columnMallocFailure(pStmt); return val; } -SQLITE_API sqlite_int64 sqlite3_column_int64(sqlite3_stmt *pStmt, int i){ +SQLITE_API sqlite_int64 SQLITE_STDCALL sqlite3_column_int64(sqlite3_stmt *pStmt, int i){ sqlite_int64 val = sqlite3_value_int64( columnMem(pStmt,i) ); columnMallocFailure(pStmt); return val; } -SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt *pStmt, int i){ +SQLITE_API const unsigned char *SQLITE_STDCALL sqlite3_column_text(sqlite3_stmt *pStmt, int i){ const unsigned char *val = sqlite3_value_text( columnMem(pStmt,i) ); columnMallocFailure(pStmt); return val; } -SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt *pStmt, int i){ +SQLITE_API sqlite3_value *SQLITE_STDCALL sqlite3_column_value(sqlite3_stmt *pStmt, int i){ Mem *pOut = columnMem(pStmt, i); if( pOut->flags&MEM_Static ){ pOut->flags &= ~MEM_Static; @@ -69268,13 +69936,13 @@ SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt *pStmt, int i){ return (sqlite3_value *)pOut; } #ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlite3_column_text16(sqlite3_stmt *pStmt, int i){ +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_text16(sqlite3_stmt *pStmt, int i){ const void *val = sqlite3_value_text16( columnMem(pStmt,i) ); columnMallocFailure(pStmt); return val; } #endif /* SQLITE_OMIT_UTF16 */ -SQLITE_API int sqlite3_column_type(sqlite3_stmt *pStmt, int i){ +SQLITE_API int SQLITE_STDCALL sqlite3_column_type(sqlite3_stmt *pStmt, int i){ int iType = sqlite3_value_type( columnMem(pStmt,i) ); columnMallocFailure(pStmt); return iType; @@ -69338,12 +70006,12 @@ static const void *columnName( ** Return the name of the Nth column of the result set returned by SQL ** statement pStmt. */ -SQLITE_API const char *sqlite3_column_name(sqlite3_stmt *pStmt, int N){ +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_name(sqlite3_stmt *pStmt, int N){ return columnName( pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_NAME); } #ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt *pStmt, int N){ +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_name16(sqlite3_stmt *pStmt, int N){ return columnName( pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_NAME); } @@ -69363,12 +70031,12 @@ SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt *pStmt, int N){ ** Return the column declaration type (if applicable) of the 'i'th column ** of the result set of SQL statement pStmt. */ -SQLITE_API const char *sqlite3_column_decltype(sqlite3_stmt *pStmt, int N){ +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_decltype(sqlite3_stmt *pStmt, int N){ return columnName( pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_DECLTYPE); } #ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt *pStmt, int N){ +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_decltype16(sqlite3_stmt *pStmt, int N){ return columnName( pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_DECLTYPE); } @@ -69381,12 +70049,12 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt *pStmt, int N){ ** NULL is returned if the result column is an expression or constant or ** anything else which is not an unambiguous reference to a database column. */ -SQLITE_API const char *sqlite3_column_database_name(sqlite3_stmt *pStmt, int N){ +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_database_name(sqlite3_stmt *pStmt, int N){ return columnName( pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_DATABASE); } #ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt *pStmt, int N){ +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_database_name16(sqlite3_stmt *pStmt, int N){ return columnName( pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_DATABASE); } @@ -69397,12 +70065,12 @@ SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt *pStmt, int N ** NULL is returned if the result column is an expression or constant or ** anything else which is not an unambiguous reference to a database column. */ -SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt *pStmt, int N){ +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_table_name(sqlite3_stmt *pStmt, int N){ return columnName( pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_TABLE); } #ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt *pStmt, int N){ +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_table_name16(sqlite3_stmt *pStmt, int N){ return columnName( pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_TABLE); } @@ -69413,12 +70081,12 @@ SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt *pStmt, int N){ ** NULL is returned if the result column is an expression or constant or ** anything else which is not an unambiguous reference to a database column. */ -SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt *pStmt, int N){ +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_origin_name(sqlite3_stmt *pStmt, int N){ return columnName( pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_COLUMN); } #ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt *pStmt, int N){ +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_origin_name16(sqlite3_stmt *pStmt, int N){ return columnName( pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_COLUMN); } @@ -69519,7 +70187,7 @@ static int bindText( /* ** Bind a blob value to an SQL statement variable. */ -SQLITE_API int sqlite3_bind_blob( +SQLITE_API int SQLITE_STDCALL sqlite3_bind_blob( sqlite3_stmt *pStmt, int i, const void *zData, @@ -69528,7 +70196,7 @@ SQLITE_API int sqlite3_bind_blob( ){ return bindText(pStmt, i, zData, nData, xDel, 0); } -SQLITE_API int sqlite3_bind_blob64( +SQLITE_API int SQLITE_STDCALL sqlite3_bind_blob64( sqlite3_stmt *pStmt, int i, const void *zData, @@ -69542,7 +70210,7 @@ SQLITE_API int sqlite3_bind_blob64( return bindText(pStmt, i, zData, (int)nData, xDel, 0); } } -SQLITE_API int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){ +SQLITE_API int SQLITE_STDCALL sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){ int rc; Vdbe *p = (Vdbe *)pStmt; rc = vdbeUnbind(p, i); @@ -69552,10 +70220,10 @@ SQLITE_API int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){ } return rc; } -SQLITE_API int sqlite3_bind_int(sqlite3_stmt *p, int i, int iValue){ +SQLITE_API int SQLITE_STDCALL sqlite3_bind_int(sqlite3_stmt *p, int i, int iValue){ return sqlite3_bind_int64(p, i, (i64)iValue); } -SQLITE_API int sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValue){ +SQLITE_API int SQLITE_STDCALL sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValue){ int rc; Vdbe *p = (Vdbe *)pStmt; rc = vdbeUnbind(p, i); @@ -69565,7 +70233,7 @@ SQLITE_API int sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValu } return rc; } -SQLITE_API int sqlite3_bind_null(sqlite3_stmt *pStmt, int i){ +SQLITE_API int SQLITE_STDCALL sqlite3_bind_null(sqlite3_stmt *pStmt, int i){ int rc; Vdbe *p = (Vdbe*)pStmt; rc = vdbeUnbind(p, i); @@ -69574,7 +70242,7 @@ SQLITE_API int sqlite3_bind_null(sqlite3_stmt *pStmt, int i){ } return rc; } -SQLITE_API int sqlite3_bind_text( +SQLITE_API int SQLITE_STDCALL sqlite3_bind_text( sqlite3_stmt *pStmt, int i, const char *zData, @@ -69583,7 +70251,7 @@ SQLITE_API int sqlite3_bind_text( ){ return bindText(pStmt, i, zData, nData, xDel, SQLITE_UTF8); } -SQLITE_API int sqlite3_bind_text64( +SQLITE_API int SQLITE_STDCALL sqlite3_bind_text64( sqlite3_stmt *pStmt, int i, const char *zData, @@ -69600,7 +70268,7 @@ SQLITE_API int sqlite3_bind_text64( } } #ifndef SQLITE_OMIT_UTF16 -SQLITE_API int sqlite3_bind_text16( +SQLITE_API int SQLITE_STDCALL sqlite3_bind_text16( sqlite3_stmt *pStmt, int i, const void *zData, @@ -69610,7 +70278,7 @@ SQLITE_API int sqlite3_bind_text16( return bindText(pStmt, i, zData, nData, xDel, SQLITE_UTF16NATIVE); } #endif /* SQLITE_OMIT_UTF16 */ -SQLITE_API int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_value *pValue){ +SQLITE_API int SQLITE_STDCALL sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_value *pValue){ int rc; switch( sqlite3_value_type((sqlite3_value*)pValue) ){ case SQLITE_INTEGER: { @@ -69641,7 +70309,7 @@ SQLITE_API int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_valu } return rc; } -SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){ +SQLITE_API int SQLITE_STDCALL sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){ int rc; Vdbe *p = (Vdbe *)pStmt; rc = vdbeUnbind(p, i); @@ -69656,7 +70324,7 @@ SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){ ** Return the number of wildcards that can be potentially bound to. ** This routine is added to support DBD::SQLite. */ -SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt *pStmt){ +SQLITE_API int SQLITE_STDCALL sqlite3_bind_parameter_count(sqlite3_stmt *pStmt){ Vdbe *p = (Vdbe*)pStmt; return p ? p->nVar : 0; } @@ -69667,7 +70335,7 @@ SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt *pStmt){ ** ** The result is always UTF-8. */ -SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt *pStmt, int i){ +SQLITE_API const char *SQLITE_STDCALL sqlite3_bind_parameter_name(sqlite3_stmt *pStmt, int i){ Vdbe *p = (Vdbe*)pStmt; if( p==0 || i<1 || i>p->nzVar ){ return 0; @@ -69695,7 +70363,7 @@ SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe *p, const char *zName, int nNa } return 0; } -SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt *pStmt, const char *zName){ +SQLITE_API int SQLITE_STDCALL sqlite3_bind_parameter_index(sqlite3_stmt *pStmt, const char *zName){ return sqlite3VdbeParameterIndex((Vdbe*)pStmt, zName, sqlite3Strlen30(zName)); } @@ -69729,7 +70397,7 @@ SQLITE_PRIVATE int sqlite3TransferBindings(sqlite3_stmt *pFromStmt, sqlite3_stmt ** an SQLITE_ERROR is returned. Nothing else can go wrong, so otherwise ** SQLITE_OK is returned. */ -SQLITE_API int sqlite3_transfer_bindings(sqlite3_stmt *pFromStmt, sqlite3_stmt *pToStmt){ +SQLITE_API int SQLITE_STDCALL sqlite3_transfer_bindings(sqlite3_stmt *pFromStmt, sqlite3_stmt *pToStmt){ Vdbe *pFrom = (Vdbe*)pFromStmt; Vdbe *pTo = (Vdbe*)pToStmt; if( pFrom->nVar!=pTo->nVar ){ @@ -69751,7 +70419,7 @@ SQLITE_API int sqlite3_transfer_bindings(sqlite3_stmt *pFromStmt, sqlite3_stmt * ** the first argument to the sqlite3_prepare() that was used to create ** the statement in the first place. */ -SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt *pStmt){ +SQLITE_API sqlite3 *SQLITE_STDCALL sqlite3_db_handle(sqlite3_stmt *pStmt){ return pStmt ? ((Vdbe*)pStmt)->db : 0; } @@ -69759,14 +70427,14 @@ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt *pStmt){ ** Return true if the prepared statement is guaranteed to not modify the ** database. */ -SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt){ +SQLITE_API int SQLITE_STDCALL sqlite3_stmt_readonly(sqlite3_stmt *pStmt){ return pStmt ? ((Vdbe*)pStmt)->readOnly : 1; } /* ** Return true if the prepared statement is in need of being reset. */ -SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt *pStmt){ +SQLITE_API int SQLITE_STDCALL sqlite3_stmt_busy(sqlite3_stmt *pStmt){ Vdbe *v = (Vdbe*)pStmt; return v!=0 && v->pc>=0 && v->magic==VDBE_MAGIC_RUN; } @@ -69777,7 +70445,7 @@ SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt *pStmt){ ** prepared statement for the database connection. Return NULL if there ** are no more. */ -SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt){ +SQLITE_API sqlite3_stmt *SQLITE_STDCALL sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt){ sqlite3_stmt *pNext; #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(pDb) ){ @@ -69798,7 +70466,7 @@ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt){ /* ** Return the value of a status counter for a prepared statement */ -SQLITE_API int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){ +SQLITE_API int SQLITE_STDCALL sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){ Vdbe *pVdbe = (Vdbe*)pStmt; u32 v; #ifdef SQLITE_ENABLE_API_ARMOR @@ -69816,7 +70484,7 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){ /* ** Return status data for a single loop within query pStmt. */ -SQLITE_API int sqlite3_stmt_scanstatus( +SQLITE_API int SQLITE_STDCALL sqlite3_stmt_scanstatus( sqlite3_stmt *pStmt, /* Prepared statement being queried */ int idx, /* Index of loop to report on */ int iScanStatusOp, /* Which metric to return */ @@ -69875,7 +70543,7 @@ SQLITE_API int sqlite3_stmt_scanstatus( /* ** Zero all counters associated with the sqlite3_stmt_scanstatus() data. */ -SQLITE_API void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){ +SQLITE_API void SQLITE_STDCALL sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){ Vdbe *p = (Vdbe*)pStmt; memset(p->anExec, 0, p->nOp * sizeof(i64)); } @@ -69967,9 +70635,8 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql( char zBase[100]; /* Initial working space */ db = p->db; - sqlite3StrAccumInit(&out, zBase, sizeof(zBase), + sqlite3StrAccumInit(&out, db, zBase, sizeof(zBase), db->aLimit[SQLITE_LIMIT_LENGTH]); - out.db = db; if( db->nVdbeExec>1 ){ while( *zRawSql ){ const char *zStart = zRawSql; @@ -69978,6 +70645,8 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql( assert( (zRawSql - zStart) > 0 ); sqlite3StrAccumAppend(&out, zStart, (int)(zRawSql-zStart)); } + }else if( p->nVar==0 ){ + sqlite3StrAccumAppend(&out, zRawSql, sqlite3Strlen30(zRawSql)); }else{ while( zRawSql[0] ){ n = findNextHostParameter(zRawSql, &nToken); @@ -69994,10 +70663,12 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql( idx = nextIndex; } }else{ - assert( zRawSql[0]==':' || zRawSql[0]=='$' || zRawSql[0]=='@' ); + assert( zRawSql[0]==':' || zRawSql[0]=='$' || + zRawSql[0]=='@' || zRawSql[0]=='#' ); testcase( zRawSql[0]==':' ); testcase( zRawSql[0]=='$' ); testcase( zRawSql[0]=='@' ); + testcase( zRawSql[0]=='#' ); idx = sqlite3VdbeParameterIndex(p, zRawSql, nToken); assert( idx>0 ); } @@ -70374,7 +71045,7 @@ static void applyAffinity( ** is appropriate. But only do the conversion if it is possible without ** loss of information and return the revised type of the argument. */ -SQLITE_API int sqlite3_value_numeric_type(sqlite3_value *pVal){ +SQLITE_API int SQLITE_STDCALL sqlite3_value_numeric_type(sqlite3_value *pVal){ int eType = sqlite3_value_type(pVal); if( eType==SQLITE_TEXT ){ Mem *pMem = (Mem*)pVal; @@ -70672,6 +71343,21 @@ static int checkSavepointCount(sqlite3 *db){ } #endif +/* +** Return the register of pOp->p2 after first preparing it to be +** overwritten with an integer value. +*/ +static Mem *out2Prerelease(Vdbe *p, VdbeOp *pOp){ + Mem *pOut; + assert( pOp->p2>0 ); + assert( pOp->p2<=(p->nMem-p->nCursor) ); + pOut = &p->aMem[pOp->p2]; + memAboutToChange(p, pOut); + if( VdbeMemDynamic(pOut) ) sqlite3VdbeMemSetNull(pOut); + pOut->flags = MEM_Int; + return pOut; +} + /* ** Execute as much of a VDBE program as we can. @@ -70680,9 +71366,11 @@ static int checkSavepointCount(sqlite3 *db){ SQLITE_PRIVATE int sqlite3VdbeExec( Vdbe *p /* The VDBE */ ){ - int pc=0; /* The program counter */ Op *aOp = p->aOp; /* Copy of p->aOp */ - Op *pOp; /* Current operation */ + Op *pOp = aOp; /* Current operation */ +#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) + Op *pOrigOp; /* Value of pOp at the top of the loop */ +#endif int rc = SQLITE_OK; /* Value to return */ sqlite3 *db = p->db; /* The database */ u8 resetSchemaOnFault = 0; /* Reset schema after an error if positive */ @@ -70758,23 +71446,22 @@ SQLITE_PRIVATE int sqlite3VdbeExec( } sqlite3EndBenignMalloc(); #endif - for(pc=p->pc; rc==SQLITE_OK; pc++){ - assert( pc>=0 && pcnOp ); + for(pOp=&aOp[p->pc]; rc==SQLITE_OK; pOp++){ + assert( pOp>=aOp && pOp<&aOp[p->nOp]); if( db->mallocFailed ) goto no_mem; #ifdef VDBE_PROFILE start = sqlite3Hwtime(); #endif nVmStep++; - pOp = &aOp[pc]; #ifdef SQLITE_ENABLE_STMT_SCANSTATUS - if( p->anExec ) p->anExec[pc]++; + if( p->anExec ) p->anExec[(int)(pOp-aOp)]++; #endif /* Only allow tracing if SQLITE_DEBUG is defined. */ #ifdef SQLITE_DEBUG if( db->flags & SQLITE_VdbeTrace ){ - sqlite3VdbePrintOp(stdout, pc, pOp); + sqlite3VdbePrintOp(stdout, (int)(pOp - aOp), pOp); } #endif @@ -70791,23 +71478,9 @@ SQLITE_PRIVATE int sqlite3VdbeExec( } #endif - /* On any opcode with the "out2-prerelease" tag, free any - ** external allocations out of mem[p2] and set mem[p2] to be - ** an undefined integer. Opcodes will either fill in the integer - ** value or convert mem[p2] to a different type. - */ - assert( pOp->opflags==sqlite3OpcodeProperty[pOp->opcode] ); - if( pOp->opflags & OPFLG_OUT2_PRERELEASE ){ - assert( pOp->p2>0 ); - assert( pOp->p2<=(p->nMem-p->nCursor) ); - pOut = &aMem[pOp->p2]; - memAboutToChange(p, pOut); - if( VdbeMemDynamic(pOut) ) sqlite3VdbeMemSetNull(pOut); - pOut->flags = MEM_Int; - } - /* Sanity checking on other operands */ #ifdef SQLITE_DEBUG + assert( pOp->opflags==sqlite3OpcodeProperty[pOp->opcode] ); if( (pOp->opflags & OPFLG_IN1)!=0 ){ assert( pOp->p1>0 ); assert( pOp->p1<=(p->nMem-p->nCursor) ); @@ -70840,6 +71513,9 @@ SQLITE_PRIVATE int sqlite3VdbeExec( memAboutToChange(p, &aMem[pOp->p3]); } #endif +#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) + pOrigOp = pOp; +#endif switch( pOp->opcode ){ @@ -70863,7 +71539,7 @@ SQLITE_PRIVATE int sqlite3VdbeExec( ** ** Other keywords in the comment that follows each case are used to ** construct the OPFLG_INITIALIZER value that initializes opcodeProperty[]. -** Keywords include: in1, in2, in3, out2_prerelease, out2, out3. See +** Keywords include: in1, in2, in3, out2, out3. See ** the mkopcodeh.awk script for additional information. ** ** Documentation about VDBE opcodes is generated by scanning this file @@ -70891,7 +71567,8 @@ SQLITE_PRIVATE int sqlite3VdbeExec( ** to the current line should be indented for EXPLAIN output. */ case OP_Goto: { /* jump */ - pc = pOp->p2 - 1; +jump_to_p2_and_check_for_interrupt: + pOp = &aOp[pOp->p2 - 1]; /* Opcodes that are used as the bottom of a loop (OP_Next, OP_Prev, ** OP_VNext, OP_RowSetNext, or OP_SorterNext) all jump here upon @@ -70936,9 +71613,13 @@ case OP_Gosub: { /* jump */ assert( VdbeMemDynamic(pIn1)==0 ); memAboutToChange(p, pIn1); pIn1->flags = MEM_Int; - pIn1->u.i = pc; + pIn1->u.i = (int)(pOp-aOp); REGISTER_TRACE(pOp->p1, pIn1); - pc = pOp->p2 - 1; + + /* Most jump operations do a goto to this spot in order to update + ** the pOp pointer. */ +jump_to_p2: + pOp = &aOp[pOp->p2 - 1]; break; } @@ -70950,7 +71631,7 @@ case OP_Gosub: { /* jump */ case OP_Return: { /* in1 */ pIn1 = &aMem[pOp->p1]; assert( pIn1->flags==MEM_Int ); - pc = (int)pIn1->u.i; + pOp = &aOp[pIn1->u.i]; pIn1->flags = MEM_Undefined; break; } @@ -70974,7 +71655,7 @@ case OP_InitCoroutine: { /* jump */ assert( !VdbeMemDynamic(pOut) ); pOut->u.i = pOp->p3 - 1; pOut->flags = MEM_Int; - if( pOp->p2 ) pc = pOp->p2 - 1; + if( pOp->p2 ) goto jump_to_p2; break; } @@ -70994,7 +71675,7 @@ case OP_EndCoroutine: { /* in1 */ pCaller = &aOp[pIn1->u.i]; assert( pCaller->opcode==OP_Yield ); assert( pCaller->p2>=0 && pCaller->p2nOp ); - pc = pCaller->p2 - 1; + pOp = &aOp[pCaller->p2 - 1]; pIn1->flags = MEM_Undefined; break; } @@ -71018,9 +71699,9 @@ case OP_Yield: { /* in1, jump */ assert( VdbeMemDynamic(pIn1)==0 ); pIn1->flags = MEM_Int; pcDest = (int)pIn1->u.i; - pIn1->u.i = pc; + pIn1->u.i = (int)(pOp - aOp); REGISTER_TRACE(pOp->p1, pIn1); - pc = pcDest; + pOp = &aOp[pcDest]; break; } @@ -71071,30 +71752,34 @@ case OP_HaltIfNull: { /* in3 */ case OP_Halt: { const char *zType; const char *zLogFmt; + VdbeFrame *pFrame; + int pcx; + pcx = (int)(pOp - aOp); if( pOp->p1==SQLITE_OK && p->pFrame ){ /* Halt the sub-program. Return control to the parent frame. */ - VdbeFrame *pFrame = p->pFrame; + pFrame = p->pFrame; p->pFrame = pFrame->pParent; p->nFrame--; sqlite3VdbeSetChanges(db, p->nChange); - pc = sqlite3VdbeFrameRestore(pFrame); + pcx = sqlite3VdbeFrameRestore(pFrame); lastRowid = db->lastRowid; if( pOp->p2==OE_Ignore ){ - /* Instruction pc is the OP_Program that invoked the sub-program + /* Instruction pcx is the OP_Program that invoked the sub-program ** currently being halted. If the p2 instruction of this OP_Halt ** instruction is set to OE_Ignore, then the sub-program is throwing ** an IGNORE exception. In this case jump to the address specified ** as the p2 of the calling OP_Program. */ - pc = p->aOp[pc].p2-1; + pcx = p->aOp[pcx].p2-1; } aOp = p->aOp; aMem = p->aMem; + pOp = &aOp[pcx]; break; } p->rc = pOp->p1; p->errorAction = (u8)pOp->p2; - p->pc = pc; + p->pc = pcx; if( p->rc ){ if( pOp->p5 ){ static const char * const azType[] = { "NOT NULL", "UNIQUE", "CHECK", @@ -71118,7 +71803,7 @@ case OP_Halt: { }else{ sqlite3SetString(&p->zErrMsg, db, "%s constraint failed", zType); } - sqlite3_log(pOp->p1, zLogFmt, pc, p->zSql, p->zErrMsg); + sqlite3_log(pOp->p1, zLogFmt, pcx, p->zSql, p->zErrMsg); } rc = sqlite3VdbeHalt(p); assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR ); @@ -71137,7 +71822,8 @@ case OP_Halt: { ** ** The 32-bit integer value P1 is written into register P2. */ -case OP_Integer: { /* out2-prerelease */ +case OP_Integer: { /* out2 */ + pOut = out2Prerelease(p, pOp); pOut->u.i = pOp->p1; break; } @@ -71148,7 +71834,8 @@ case OP_Integer: { /* out2-prerelease */ ** P4 is a pointer to a 64-bit integer value. ** Write that value into register P2. */ -case OP_Int64: { /* out2-prerelease */ +case OP_Int64: { /* out2 */ + pOut = out2Prerelease(p, pOp); assert( pOp->p4.pI64!=0 ); pOut->u.i = *pOp->p4.pI64; break; @@ -71161,7 +71848,8 @@ case OP_Int64: { /* out2-prerelease */ ** P4 is a pointer to a 64-bit floating point value. ** Write that value into register P2. */ -case OP_Real: { /* same as TK_FLOAT, out2-prerelease */ +case OP_Real: { /* same as TK_FLOAT, out2 */ + pOut = out2Prerelease(p, pOp); pOut->flags = MEM_Real; assert( !sqlite3IsNaN(*pOp->p4.pReal) ); pOut->u.r = *pOp->p4.pReal; @@ -71173,12 +71861,13 @@ case OP_Real: { /* same as TK_FLOAT, out2-prerelease */ ** Synopsis: r[P2]='P4' ** ** P4 points to a nul terminated UTF-8 string. This opcode is transformed -** into a String before it is executed for the first time. During +** into a String opcode before it is executed for the first time. During ** this transformation, the length of string P4 is computed and stored ** as the P1 parameter. */ -case OP_String8: { /* same as TK_STRING, out2-prerelease */ +case OP_String8: { /* same as TK_STRING, out2 */ assert( pOp->p4.z!=0 ); + pOut = out2Prerelease(p, pOp); pOp->opcode = OP_String; pOp->p1 = sqlite3Strlen30(pOp->p4.z); @@ -71205,18 +71894,31 @@ case OP_String8: { /* same as TK_STRING, out2-prerelease */ /* Fall through to the next case, OP_String */ } -/* Opcode: String P1 P2 * P4 * +/* Opcode: String P1 P2 P3 P4 P5 ** Synopsis: r[P2]='P4' (len=P1) ** ** The string value P4 of length P1 (bytes) is stored in register P2. +** +** If P5!=0 and the content of register P3 is greater than zero, then +** the datatype of the register P2 is converted to BLOB. The content is +** the same sequence of bytes, it is merely interpreted as a BLOB instead +** of a string, as if it had been CAST. */ -case OP_String: { /* out2-prerelease */ +case OP_String: { /* out2 */ assert( pOp->p4.z!=0 ); + pOut = out2Prerelease(p, pOp); pOut->flags = MEM_Str|MEM_Static|MEM_Term; pOut->z = pOp->p4.z; pOut->n = pOp->p1; pOut->enc = encoding; UPDATE_MAX_BLOBSIZE(pOut); + if( pOp->p5 ){ + assert( pOp->p3>0 ); + assert( pOp->p3<=(p->nMem-p->nCursor) ); + pIn3 = &aMem[pOp->p3]; + assert( pIn3->flags & MEM_Int ); + if( pIn3->u.i ) pOut->flags = MEM_Blob|MEM_Static|MEM_Term; + } break; } @@ -71232,9 +71934,10 @@ case OP_String: { /* out2-prerelease */ ** NULL values will not compare equal even if SQLITE_NULLEQ is set on ** OP_Ne or OP_Eq. */ -case OP_Null: { /* out2-prerelease */ +case OP_Null: { /* out2 */ int cnt; u16 nullFlag; + pOut = out2Prerelease(p, pOp); cnt = pOp->p3-pOp->p2; assert( pOp->p3<=(p->nMem-p->nCursor) ); pOut->flags = nullFlag = pOp->p1 ? (MEM_Null|MEM_Cleared) : MEM_Null; @@ -71269,8 +71972,9 @@ case OP_SoftNull: { ** P4 points to a blob of data P1 bytes long. Store this ** blob in register P2. */ -case OP_Blob: { /* out2-prerelease */ +case OP_Blob: { /* out2 */ assert( pOp->p1 <= SQLITE_MAX_LENGTH ); + pOut = out2Prerelease(p, pOp); sqlite3VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0); pOut->enc = encoding; UPDATE_MAX_BLOBSIZE(pOut); @@ -71285,7 +71989,7 @@ case OP_Blob: { /* out2-prerelease */ ** If the parameter is named, then its name appears in P4. ** The P4 value is used by sqlite3_bind_parameter_name(). */ -case OP_Variable: { /* out2-prerelease */ +case OP_Variable: { /* out2 */ Mem *pVar; /* Value being transferred */ assert( pOp->p1>0 && pOp->p1<=p->nVar ); @@ -71294,6 +71998,7 @@ case OP_Variable: { /* out2-prerelease */ if( sqlite3VdbeMemTooBig(pVar) ){ goto too_big; } + pOut = out2Prerelease(p, pOp); sqlite3VdbeMemShallowCopy(pOut, pVar, MEM_Static); UPDATE_MAX_BLOBSIZE(pOut); break; @@ -71328,10 +72033,11 @@ case OP_Move: { memAboutToChange(p, pOut); sqlite3VdbeMemMove(pOut, pIn1); #ifdef SQLITE_DEBUG - if( pOut->pScopyFrom>=&aMem[p1] && pOut->pScopyFrom<&aMem[p1+pOp->p3] ){ - pOut->pScopyFrom += p1 - pOp->p2; + if( pOut->pScopyFrom>=&aMem[p1] && pOut->pScopyFrompScopyFrom += pOp->p2 - p1; } #endif + Deephemeralize(pOut); REGISTER_TRACE(p2++, pOut); pIn1++; pOut++; @@ -71470,7 +72176,7 @@ case OP_ResultRow: { /* Return SQLITE_ROW */ - p->pc = pc + 1; + p->pc = (int)(pOp - aOp) + 1; rc = SQLITE_ROW; goto vdbe_return; } @@ -71663,7 +72369,7 @@ arithmetic_result_is_null: ** ** The interface used by the implementation of the aforementioned functions ** to retrieve the collation sequence set by this opcode is not available -** publicly, only to user functions defined in func.c. +** publicly. Only built-in functions have access to this feature. */ case OP_CollSeq: { assert( pOp->p4type==P4_COLLSEQ ); @@ -71716,7 +72422,7 @@ case OP_Function: { assert( pOp->p4type==P4_FUNCDEF ); ctx.pFunc = pOp->p4.pFunc; - ctx.iOp = pc; + ctx.iOp = (int)(pOp - aOp); ctx.pVdbe = p; MemSetTypeFlag(ctx.pOut, MEM_Null); ctx.fErrorOrAux = 0; @@ -71730,7 +72436,7 @@ case OP_Function: { sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(ctx.pOut)); rc = ctx.isError; } - sqlite3VdbeDeleteAuxData(p, pc, pOp->p1); + sqlite3VdbeDeleteAuxData(p, (int)(pOp - aOp), pOp->p1); } /* Copy the result of the function into register P3 */ @@ -71859,8 +72565,7 @@ case OP_MustBeInt: { /* jump, in1 */ rc = SQLITE_MISMATCH; goto abort_due_to_error; }else{ - pc = pOp->p2 - 1; - break; + goto jump_to_p2; } } } @@ -72046,7 +72751,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ }else{ VdbeBranchTaken(2,3); if( pOp->p5 & SQLITE_JUMPIFNULL ){ - pc = pOp->p2-1; + goto jump_to_p2; } } break; @@ -72066,11 +72771,15 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ testcase( pIn1->flags & MEM_Int ); testcase( pIn1->flags & MEM_Real ); sqlite3VdbeMemStringify(pIn1, encoding, 1); + testcase( (flags1&MEM_Dyn) != (pIn1->flags&MEM_Dyn) ); + flags1 = (pIn1->flags & ~MEM_TypeMask) | (flags1 & MEM_TypeMask); } if( (pIn3->flags & MEM_Str)==0 && (pIn3->flags & (MEM_Int|MEM_Real))!=0 ){ testcase( pIn3->flags & MEM_Int ); testcase( pIn3->flags & MEM_Real ); sqlite3VdbeMemStringify(pIn3, encoding, 1); + testcase( (flags3&MEM_Dyn) != (pIn3->flags&MEM_Dyn) ); + flags3 = (pIn3->flags & ~MEM_TypeMask) | (flags3 & MEM_TypeMask); } } assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 ); @@ -72094,6 +72803,12 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ default: res = res>=0; break; } + /* Undo any changes made by applyAffinity() to the input registers. */ + assert( (pIn1->flags & MEM_Dyn) == (flags1 & MEM_Dyn) ); + pIn1->flags = flags1; + assert( (pIn3->flags & MEM_Dyn) == (flags3 & MEM_Dyn) ); + pIn3->flags = flags3; + if( pOp->p5 & SQLITE_STOREP2 ){ pOut = &aMem[pOp->p2]; memAboutToChange(p, pOut); @@ -72103,12 +72818,9 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ }else{ VdbeBranchTaken(res!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3); if( res ){ - pc = pOp->p2-1; + goto jump_to_p2; } } - /* Undo any changes made by applyAffinity() to the input registers. */ - pIn1->flags = flags1; - pIn3->flags = flags3; break; } @@ -72203,11 +72915,11 @@ case OP_Compare: { */ case OP_Jump: { /* jump */ if( iCompare<0 ){ - pc = pOp->p1 - 1; VdbeBranchTaken(0,3); + VdbeBranchTaken(0,3); pOp = &aOp[pOp->p1 - 1]; }else if( iCompare==0 ){ - pc = pOp->p2 - 1; VdbeBranchTaken(1,3); + VdbeBranchTaken(1,3); pOp = &aOp[pOp->p2 - 1]; }else{ - pc = pOp->p3 - 1; VdbeBranchTaken(2,3); + VdbeBranchTaken(2,3); pOp = &aOp[pOp->p3 - 1]; } break; } @@ -72317,7 +73029,7 @@ case OP_Once: { /* jump */ assert( pOp->p1nOnceFlag ); VdbeBranchTaken(p->aOnceFlag[pOp->p1]!=0, 2); if( p->aOnceFlag[pOp->p1] ){ - pc = pOp->p2-1; + goto jump_to_p2; }else{ p->aOnceFlag[pOp->p1] = 1; } @@ -72352,7 +73064,7 @@ case OP_IfNot: { /* jump, in1 */ } VdbeBranchTaken(c!=0, 2); if( c ){ - pc = pOp->p2-1; + goto jump_to_p2; } break; } @@ -72366,7 +73078,7 @@ case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */ pIn1 = &aMem[pOp->p1]; VdbeBranchTaken( (pIn1->flags & MEM_Null)!=0, 2); if( (pIn1->flags & MEM_Null)!=0 ){ - pc = pOp->p2 - 1; + goto jump_to_p2; } break; } @@ -72380,7 +73092,7 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */ pIn1 = &aMem[pOp->p1]; VdbeBranchTaken( (pIn1->flags & MEM_Null)==0, 2); if( (pIn1->flags & MEM_Null)==0 ){ - pc = pOp->p2 - 1; + goto jump_to_p2; } break; } @@ -72594,7 +73306,7 @@ case OP_Column: { } } - /* If after trying to extra new entries from the header, nHdrParsed is + /* If after trying to extract new entries from the header, nHdrParsed is ** still not up to p2, that means that the record has fewer than p2 ** columns. So the result will be either the default value or a NULL. */ @@ -72718,7 +73430,7 @@ case OP_MakeRecord: { u64 nData; /* Number of bytes of data space */ int nHdr; /* Number of bytes of header space */ i64 nByte; /* Data space required for this record */ - int nZero; /* Number of zero bytes at the end of the record */ + i64 nZero; /* Number of zero bytes at the end of the record */ int nVarint; /* Number of bytes in a varint */ u32 serial_type; /* Type field */ Mem *pData0; /* First field to be combined into the record */ @@ -72810,7 +73522,7 @@ case OP_MakeRecord: { if( nVarintdb->aLimit[SQLITE_LIMIT_LENGTH] ){ + if( nByte+nZero>db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; } @@ -72861,7 +73573,7 @@ case OP_MakeRecord: { ** opened by cursor P1 in register P2 */ #ifndef SQLITE_OMIT_BTREECOUNT -case OP_Count: { /* out2-prerelease */ +case OP_Count: { /* out2 */ i64 nEntry; BtCursor *pCrsr; @@ -72869,6 +73581,7 @@ case OP_Count: { /* out2-prerelease */ assert( pCrsr ); nEntry = 0; /* Not needed. Only used to silence a warning. */ rc = sqlite3BtreeCount(pCrsr, &nEntry); + pOut = out2Prerelease(p, pOp); pOut->u.i = nEntry; break; } @@ -72982,7 +73695,7 @@ case OP_Savepoint: { } db->autoCommit = 1; if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){ - p->pc = pc; + p->pc = (int)(pOp - aOp); db->autoCommit = 0; p->rc = rc = SQLITE_BUSY; goto vdbe_return; @@ -73041,7 +73754,7 @@ case OP_Savepoint: { db->nDeferredImmCons = pSavepoint->nDeferredImmCons; } - if( !isTransaction ){ + if( !isTransaction || p1==SAVEPOINT_ROLLBACK ){ rc = sqlite3VtabSavepoint(db, p1, iSavepoint); if( rc!=SQLITE_OK ) goto abort_due_to_error; } @@ -73101,7 +73814,7 @@ case OP_AutoCommit: { }else{ db->autoCommit = (u8)desiredAutoCommit; if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){ - p->pc = pc; + p->pc = (int)(pOp - aOp); db->autoCommit = (u8)(1-desiredAutoCommit); p->rc = rc = SQLITE_BUSY; goto vdbe_return; @@ -73178,7 +73891,7 @@ case OP_Transaction: { if( pBt ){ rc = sqlite3BtreeBeginTrans(pBt, pOp->p2); if( rc==SQLITE_BUSY ){ - p->pc = pc; + p->pc = (int)(pOp - aOp); p->rc = rc = SQLITE_BUSY; goto vdbe_return; } @@ -73208,7 +73921,12 @@ case OP_Transaction: { p->nStmtDefImmCons = db->nDeferredImmCons; } - /* Gather the schema version number for checking */ + /* Gather the schema version number for checking: + ** IMPLEMENTATION-OF: R-32195-19465 The schema version is used by SQLite + ** each time a query is executed to ensure that the internal cache of the + ** schema used when compiling the SQL query matches the schema of the + ** database against which the compiled query is actually executed. + */ sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&iMeta); iGen = db->aDb[pOp->p1].pSchema->iGeneration; }else{ @@ -73252,7 +73970,7 @@ case OP_Transaction: { ** must be started or there must be an open cursor) before ** executing this instruction. */ -case OP_ReadCookie: { /* out2-prerelease */ +case OP_ReadCookie: { /* out2 */ int iMeta; int iDb; int iCookie; @@ -73266,6 +73984,7 @@ case OP_ReadCookie: { /* out2-prerelease */ assert( DbMaskTest(p->btreeMask, iDb) ); sqlite3BtreeGetMeta(db->aDb[iDb].pBt, iCookie, (u32 *)&iMeta); + pOut = out2Prerelease(p, pOp); pOut->u.i = iMeta; break; } @@ -73376,20 +74095,6 @@ case OP_SetCookie: { /* in3 */ ** See also OpenRead. */ case OP_ReopenIdx: { - VdbeCursor *pCur; - - assert( pOp->p5==0 ); - assert( pOp->p4type==P4_KEYINFO ); - pCur = p->apCsr[pOp->p1]; - if( pCur && pCur->pgnoRoot==(u32)pOp->p2 ){ - assert( pCur->iDb==pOp->p3 ); /* Guaranteed by the code generator */ - break; - } - /* If the cursor is not currently open or is open on a different - ** index, then fall through into OP_OpenRead to force a reopen */ -} -case OP_OpenRead: -case OP_OpenWrite: { int nField; KeyInfo *pKeyInfo; int p2; @@ -73399,8 +74104,20 @@ case OP_OpenWrite: { VdbeCursor *pCur; Db *pDb; - assert( (pOp->p5&(OPFLAG_P2ISREG|OPFLAG_BULKCSR))==pOp->p5 ); - assert( pOp->opcode==OP_OpenWrite || pOp->p5==0 ); + assert( pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ ); + assert( pOp->p4type==P4_KEYINFO ); + pCur = p->apCsr[pOp->p1]; + if( pCur && pCur->pgnoRoot==(u32)pOp->p2 ){ + assert( pCur->iDb==pOp->p3 ); /* Guaranteed by the code generator */ + goto open_cursor_set_hints; + } + /* If the cursor is not currently open or is open on a different + ** index, then fall through into OP_OpenRead to force a reopen */ +case OP_OpenRead: +case OP_OpenWrite: + + assert( (pOp->p5&(OPFLAG_P2ISREG|OPFLAG_BULKCSR|OPFLAG_SEEKEQ))==pOp->p5 ); + assert( pOp->opcode==OP_OpenWrite || pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ ); assert( p->bIsReader ); assert( pOp->opcode==OP_OpenRead || pOp->opcode==OP_ReopenIdx || p->readOnly==0 ); @@ -73463,14 +74180,17 @@ case OP_OpenWrite: { pCur->pgnoRoot = p2; rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->pCursor); pCur->pKeyInfo = pKeyInfo; - assert( OPFLAG_BULKCSR==BTREE_BULKLOAD ); - sqlite3BtreeCursorHints(pCur->pCursor, (pOp->p5 & OPFLAG_BULKCSR)); - /* Set the VdbeCursor.isTable variable. Previous versions of ** SQLite used to check if the root-page flags were sane at this point ** and report database corruption if they were not, but this check has ** since moved into the btree layer. */ pCur->isTable = pOp->p4type!=P4_KEYINFO; + +open_cursor_set_hints: + assert( OPFLAG_BULKCSR==BTREE_BULKLOAD ); + assert( OPFLAG_SEEKEQ==BTREE_SEEK_EQ ); + sqlite3BtreeCursorHints(pCur->pCursor, + (pOp->p5 & (OPFLAG_BULKCSR|OPFLAG_SEEKEQ))); break; } @@ -73586,7 +74306,7 @@ case OP_SequenceTest: { pC = p->apCsr[pOp->p1]; assert( pC->pSorter ); if( (pC->seqCount++)==0 ){ - pc = pOp->p2 - 1; + goto jump_to_p2; } break; } @@ -73731,6 +74451,22 @@ case OP_SeekGT: { /* jump, in3 */ #ifdef SQLITE_DEBUG pC->seekOp = pOp->opcode; #endif + + /* For a cursor with the BTREE_SEEK_EQ hint, only the OP_SeekGE and + ** OP_SeekLE opcodes are allowed, and these must be immediately followed + ** by an OP_IdxGT or OP_IdxLT opcode, respectively, with the same key. + */ +#ifdef SQLITE_DEBUG + if( sqlite3BtreeCursorHasHint(pC->pCursor, BTREE_SEEK_EQ) ){ + assert( pOp->opcode==OP_SeekGE || pOp->opcode==OP_SeekLE ); + assert( pOp[1].opcode==OP_IdxLT || pOp[1].opcode==OP_IdxGT ); + assert( pOp[1].p1==pOp[0].p1 ); + assert( pOp[1].p2==pOp[0].p2 ); + assert( pOp[1].p3==pOp[0].p3 ); + assert( pOp[1].p4.i==pOp[0].p4.i ); + } +#endif + if( pC->isTable ){ /* The input value in P3 might be of any type: integer, real, string, ** blob, or NULL. But it needs to be an integer before we can do @@ -73747,7 +74483,7 @@ case OP_SeekGT: { /* jump, in3 */ if( (pIn3->flags & MEM_Real)==0 ){ /* If the P3 value cannot be converted into any kind of a number, ** then the seek is not possible, so jump to P2 */ - pc = pOp->p2 - 1; VdbeBranchTaken(1,2); + VdbeBranchTaken(1,2); goto jump_to_p2; break; } @@ -73838,7 +74574,7 @@ case OP_SeekGT: { /* jump, in3 */ assert( pOp->p2>0 ); VdbeBranchTaken(res!=0,2); if( res ){ - pc = pOp->p2 - 1; + goto jump_to_p2; } break; } @@ -73932,6 +74668,7 @@ case OP_NoConflict: /* jump, in3 */ case OP_NotFound: /* jump, in3 */ case OP_Found: { /* jump, in3 */ int alreadyExists; + int takeJump; int ii; VdbeCursor *pC; int res; @@ -73954,7 +74691,7 @@ case OP_Found: { /* jump, in3 */ pIn3 = &aMem[pOp->p3]; assert( pC->pCursor!=0 ); assert( pC->isTable==0 ); - pFree = 0; /* Not needed. Only used to suppress a compiler warning. */ + pFree = 0; if( pOp->p4.i>0 ){ r.pKeyInfo = pC->pKeyInfo; r.nField = (u16)pOp->p4.i; @@ -73977,21 +74714,20 @@ case OP_Found: { /* jump, in3 */ sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, pIdxKey); } pIdxKey->default_rc = 0; + takeJump = 0; if( pOp->opcode==OP_NoConflict ){ /* For the OP_NoConflict opcode, take the jump if any of the ** input fields are NULL, since any key with a NULL will not ** conflict */ for(ii=0; iinField; ii++){ if( pIdxKey->aMem[ii].flags & MEM_Null ){ - pc = pOp->p2 - 1; VdbeBranchTaken(1,2); + takeJump = 1; break; } } } rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, pIdxKey, 0, 0, &res); - if( pOp->p4.i==0 ){ - sqlite3DbFree(db, pFree); - } + sqlite3DbFree(db, pFree); if( rc!=SQLITE_OK ){ break; } @@ -74002,10 +74738,10 @@ case OP_Found: { /* jump, in3 */ pC->cacheStatus = CACHE_STALE; if( pOp->opcode==OP_Found ){ VdbeBranchTaken(alreadyExists!=0,2); - if( alreadyExists ) pc = pOp->p2 - 1; + if( alreadyExists ) goto jump_to_p2; }else{ - VdbeBranchTaken(alreadyExists==0,2); - if( !alreadyExists ) pc = pOp->p2 - 1; + VdbeBranchTaken(takeJump||alreadyExists==0,2); + if( takeJump || !alreadyExists ) goto jump_to_p2; } break; } @@ -74054,10 +74790,8 @@ case OP_NotExists: { /* jump, in3 */ pC->cacheStatus = CACHE_STALE; pC->deferredMoveto = 0; VdbeBranchTaken(res!=0,2); - if( res!=0 ){ - pc = pOp->p2 - 1; - } pC->seekResult = res; + if( res!=0 ) goto jump_to_p2; break; } @@ -74069,9 +74803,10 @@ case OP_NotExists: { /* jump, in3 */ ** The sequence number on the cursor is incremented after this ** instruction. */ -case OP_Sequence: { /* out2-prerelease */ +case OP_Sequence: { /* out2 */ assert( pOp->p1>=0 && pOp->p1nCursor ); assert( p->apCsr[pOp->p1]!=0 ); + pOut = out2Prerelease(p, pOp); pOut->u.i = p->apCsr[pOp->p1]->seqCount++; break; } @@ -74092,7 +74827,7 @@ case OP_Sequence: { /* out2-prerelease */ ** generated record number. This P3 mechanism is used to help implement the ** AUTOINCREMENT feature. */ -case OP_NewRowid: { /* out2-prerelease */ +case OP_NewRowid: { /* out2 */ i64 v; /* The new rowid */ VdbeCursor *pC; /* Cursor of table to get the new rowid */ int res; /* Result of an sqlite3BtreeLast() */ @@ -74102,6 +74837,7 @@ case OP_NewRowid: { /* out2-prerelease */ v = 0; res = 0; + pOut = out2Prerelease(p, pOp); assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); @@ -74415,9 +75151,7 @@ case OP_SorterCompare: { res = 0; rc = sqlite3VdbeSorterCompare(pC, pIn3, nKeyCol, &res); VdbeBranchTaken(res!=0,2); - if( res ){ - pc = pOp->p2-1; - } + if( res ) goto jump_to_p2; break; }; @@ -74546,12 +75280,13 @@ case OP_RowData: { ** be a separate OP_VRowid opcode for use with virtual tables, but this ** one opcode now works for both table types. */ -case OP_Rowid: { /* out2-prerelease */ +case OP_Rowid: { /* out2 */ VdbeCursor *pC; i64 v; sqlite3_vtab *pVtab; const sqlite3_module *pModule; + pOut = out2Prerelease(p, pOp); assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); @@ -74604,7 +75339,7 @@ case OP_NullRow: { break; } -/* Opcode: Last P1 P2 * * * +/* Opcode: Last P1 P2 P3 * * ** ** The next use of the Rowid or Column or Prev instruction for P1 ** will refer to the last entry in the database table or index. @@ -74631,12 +75366,13 @@ case OP_Last: { /* jump */ pC->nullRow = (u8)res; pC->deferredMoveto = 0; pC->cacheStatus = CACHE_STALE; + pC->seekResult = pOp->p3; #ifdef SQLITE_DEBUG pC->seekOp = OP_Last; #endif if( pOp->p2>0 ){ VdbeBranchTaken(res!=0,2); - if( res ) pc = pOp->p2 - 1; + if( res ) goto jump_to_p2; } break; } @@ -74700,9 +75436,7 @@ case OP_Rewind: { /* jump */ pC->nullRow = (u8)res; assert( pOp->p2>0 && pOp->p2nOp ); VdbeBranchTaken(res!=0,2); - if( res ){ - pc = pOp->p2 - 1; - } + if( res ) goto jump_to_p2; break; } @@ -74813,11 +75547,11 @@ next_tail: VdbeBranchTaken(res==0,2); if( res==0 ){ pC->nullRow = 0; - pc = pOp->p2 - 1; p->aCounter[pOp->p5]++; #ifdef SQLITE_TEST sqlite3_search_count++; #endif + goto jump_to_p2_and_check_for_interrupt; }else{ pC->nullRow = 1; } @@ -74925,11 +75659,12 @@ case OP_IdxDelete: { ** ** See also: Rowid, MakeRecord. */ -case OP_IdxRowid: { /* out2-prerelease */ +case OP_IdxRowid: { /* out2 */ BtCursor *pCrsr; VdbeCursor *pC; i64 rowid; + pOut = out2Prerelease(p, pOp); assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); @@ -75042,9 +75777,7 @@ case OP_IdxGE: { /* jump */ res++; } VdbeBranchTaken(res>0,2); - if( res>0 ){ - pc = pOp->p2 - 1 ; - } + if( res>0 ) goto jump_to_p2; break; } @@ -75068,32 +75801,18 @@ case OP_IdxGE: { /* jump */ ** ** See also: Clear */ -case OP_Destroy: { /* out2-prerelease */ +case OP_Destroy: { /* out2 */ int iMoved; - int iCnt; - Vdbe *pVdbe; int iDb; assert( p->readOnly==0 ); -#ifndef SQLITE_OMIT_VIRTUALTABLE - iCnt = 0; - for(pVdbe=db->pVdbe; pVdbe; pVdbe = pVdbe->pNext){ - if( pVdbe->magic==VDBE_MAGIC_RUN && pVdbe->bIsReader - && pVdbe->inVtabMethod<2 && pVdbe->pc>=0 - ){ - iCnt++; - } - } -#else - iCnt = db->nVdbeRead; -#endif + pOut = out2Prerelease(p, pOp); pOut->flags = MEM_Null; - if( iCnt>1 ){ + if( db->nVdbeRead > db->nVDestroy+1 ){ rc = SQLITE_LOCKED; p->errorAction = OE_Abort; }else{ iDb = pOp->p3; - assert( iCnt==1 ); assert( DbMaskTest(p->btreeMask, iDb) ); iMoved = 0; /* Not needed. Only to silence a warning. */ rc = sqlite3BtreeDropTable(db->aDb[iDb].pBt, pOp->p1, &iMoved); @@ -75196,12 +75915,13 @@ case OP_ResetSorter: { ** ** See documentation on OP_CreateTable for additional information. */ -case OP_CreateIndex: /* out2-prerelease */ -case OP_CreateTable: { /* out2-prerelease */ +case OP_CreateIndex: /* out2 */ +case OP_CreateTable: { /* out2 */ int pgno; int flags; Db *pDb; + pOut = out2Prerelease(p, pOp); pgno = 0; assert( pOp->p1>=0 && pOp->p1nDb ); assert( DbMaskTest(p->btreeMask, pOp->p1) ); @@ -75427,12 +76147,12 @@ case OP_RowSetRead: { /* jump, in1, out3 */ ){ /* The boolean index is empty */ sqlite3VdbeMemSetNull(pIn1); - pc = pOp->p2 - 1; VdbeBranchTaken(1,2); + goto jump_to_p2_and_check_for_interrupt; }else{ /* A value was pulled from the index */ - sqlite3VdbeMemSetInt64(&aMem[pOp->p3], val); VdbeBranchTaken(0,2); + sqlite3VdbeMemSetInt64(&aMem[pOp->p3], val); } goto check_for_interrupt; } @@ -75483,10 +76203,7 @@ case OP_RowSetTest: { /* jump, in1, in3 */ if( iSet ){ exists = sqlite3RowSetTest(pIn1->u.pRowSet, iSet, pIn3->u.i); VdbeBranchTaken(exists!=0,2); - if( exists ){ - pc = pOp->p2 - 1; - break; - } + if( exists ) goto jump_to_p2; } if( iSet>=0 ){ sqlite3RowSetInsert(pIn1->u.pRowSet, pIn3->u.i); @@ -75575,7 +76292,7 @@ case OP_Program: { /* jump */ pFrame->v = p; pFrame->nChildMem = nMem; pFrame->nChildCsr = pProgram->nCsr; - pFrame->pc = pc; + pFrame->pc = (int)(pOp - aOp); pFrame->aMem = p->aMem; pFrame->nMem = p->nMem; pFrame->apCsr = p->apCsr; @@ -75598,7 +76315,7 @@ case OP_Program: { /* jump */ pFrame = pRt->u.pFrame; assert( pProgram->nMem+pProgram->nCsr==pFrame->nChildMem ); assert( pProgram->nCsr==pFrame->nChildCsr ); - assert( pc==pFrame->pc ); + assert( (int)(pOp - aOp)==pFrame->pc ); } p->nFrame++; @@ -75619,7 +76336,7 @@ case OP_Program: { /* jump */ #ifdef SQLITE_ENABLE_STMT_SCANSTATUS p->anExec = 0; #endif - pc = -1; + pOp = &aOp[-1]; memset(p->aOnceFlag, 0, p->nOnceFlag); break; @@ -75637,9 +76354,10 @@ case OP_Program: { /* jump */ ** the value of the P1 argument to the value of the P1 argument to the ** calling OP_Program instruction. */ -case OP_Param: { /* out2-prerelease */ +case OP_Param: { /* out2 */ VdbeFrame *pFrame; Mem *pIn; + pOut = out2Prerelease(p, pOp); pFrame = p->pFrame; pIn = &pFrame->aMem[pOp->p1 + pFrame->aOp[pFrame->pc].p1]; sqlite3VdbeMemShallowCopy(pOut, pIn, MEM_Ephem); @@ -75683,10 +76401,10 @@ case OP_FkCounter: { case OP_FkIfZero: { /* jump */ if( pOp->p1 ){ VdbeBranchTaken(db->nDeferredCons==0 && db->nDeferredImmCons==0, 2); - if( db->nDeferredCons==0 && db->nDeferredImmCons==0 ) pc = pOp->p2-1; + if( db->nDeferredCons==0 && db->nDeferredImmCons==0 ) goto jump_to_p2; }else{ VdbeBranchTaken(p->nFkConstraint==0 && db->nDeferredImmCons==0, 2); - if( p->nFkConstraint==0 && db->nDeferredImmCons==0 ) pc = pOp->p2-1; + if( p->nFkConstraint==0 && db->nDeferredImmCons==0 ) goto jump_to_p2; } break; } @@ -75726,18 +76444,18 @@ case OP_MemMax: { /* in2 */ /* Opcode: IfPos P1 P2 * * * ** Synopsis: if r[P1]>0 goto P2 ** -** If the value of register P1 is 1 or greater, jump to P2. +** Register P1 must contain an integer. +** If the value of register P1 is 1 or greater, jump to P2 and +** add the literal value P3 to register P1. ** -** It is illegal to use this instruction on a register that does -** not contain an integer. An assertion fault will result if you try. +** If the initial value of register P1 is less than 1, then the +** value is unchanged and control passes through to the next instruction. */ case OP_IfPos: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; assert( pIn1->flags&MEM_Int ); VdbeBranchTaken( pIn1->u.i>0, 2); - if( pIn1->u.i>0 ){ - pc = pOp->p2 - 1; - } + if( pIn1->u.i>0 ) goto jump_to_p2; break; } @@ -75752,26 +76470,56 @@ case OP_IfNeg: { /* jump, in1 */ assert( pIn1->flags&MEM_Int ); pIn1->u.i += pOp->p3; VdbeBranchTaken(pIn1->u.i<0, 2); - if( pIn1->u.i<0 ){ - pc = pOp->p2 - 1; + if( pIn1->u.i<0 ) goto jump_to_p2; + break; +} + +/* Opcode: IfNotZero P1 P2 P3 * * +** Synopsis: if r[P1]!=0 then r[P1]+=P3, goto P2 +** +** Register P1 must contain an integer. If the content of register P1 is +** initially nonzero, then add P3 to P1 and jump to P2. If register P1 is +** initially zero, leave it unchanged and fall through. +*/ +case OP_IfNotZero: { /* jump, in1 */ + pIn1 = &aMem[pOp->p1]; + assert( pIn1->flags&MEM_Int ); + VdbeBranchTaken(pIn1->u.i<0, 2); + if( pIn1->u.i ){ + pIn1->u.i += pOp->p3; + goto jump_to_p2; } break; } -/* Opcode: IfZero P1 P2 P3 * * -** Synopsis: r[P1]+=P3, if r[P1]==0 goto P2 +/* Opcode: DecrJumpZero P1 P2 * * * +** Synopsis: if (--r[P1])==0 goto P2 ** -** The register P1 must contain an integer. Add literal P3 to the -** value in register P1. If the result is exactly 0, jump to P2. +** Register P1 must hold an integer. Decrement the value in register P1 +** then jump to P2 if the new value is exactly zero. */ -case OP_IfZero: { /* jump, in1 */ +case OP_DecrJumpZero: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; assert( pIn1->flags&MEM_Int ); - pIn1->u.i += pOp->p3; + pIn1->u.i--; VdbeBranchTaken(pIn1->u.i==0, 2); - if( pIn1->u.i==0 ){ - pc = pOp->p2 - 1; - } + if( pIn1->u.i==0 ) goto jump_to_p2; + break; +} + + +/* Opcode: JumpZeroIncr P1 P2 * * * +** Synopsis: if (r[P1]++)==0 ) goto P2 +** +** The register P1 must contain an integer. If register P1 is initially +** zero, then jump to P2. Increment register P1 regardless of whether or +** not the jump is taken. +*/ +case OP_JumpZeroIncr: { /* jump, in1 */ + pIn1 = &aMem[pOp->p1]; + assert( pIn1->flags&MEM_Int ); + VdbeBranchTaken(pIn1->u.i==0, 2); + if( (pIn1->u.i++)==0 ) goto jump_to_p2; break; } @@ -75813,7 +76561,7 @@ case OP_AggStep: { ctx.pOut = &t; ctx.isError = 0; ctx.pVdbe = p; - ctx.iOp = pc; + ctx.iOp = (int)(pOp - aOp); ctx.skipFlag = 0; (ctx.pFunc->xStep)(&ctx, n, apVal); /* IMP: R-24505-23230 */ if( ctx.isError ){ @@ -75908,7 +76656,7 @@ case OP_Checkpoint: { ** ** Write a string containing the final journal-mode to register P2. */ -case OP_JournalMode: { /* out2-prerelease */ +case OP_JournalMode: { /* out2 */ Btree *pBt; /* Btree to change journal mode of */ Pager *pPager; /* Pager associated with pBt */ int eNew; /* New journal mode */ @@ -75917,6 +76665,7 @@ case OP_JournalMode: { /* out2-prerelease */ const char *zFilename; /* Name of database file for pPager */ #endif + pOut = out2Prerelease(p, pOp); eNew = pOp->p3; assert( eNew==PAGER_JOURNALMODE_DELETE || eNew==PAGER_JOURNALMODE_TRUNCATE @@ -75992,7 +76741,6 @@ case OP_JournalMode: { /* out2-prerelease */ } eNew = sqlite3PagerSetJournalMode(pPager, eNew); - pOut = &aMem[pOp->p2]; pOut->flags = MEM_Str|MEM_Static|MEM_Term; pOut->z = (char *)sqlite3JournalModename(eNew); pOut->n = sqlite3Strlen30(pOut->z); @@ -76033,8 +76781,8 @@ case OP_IncrVacuum: { /* jump */ rc = sqlite3BtreeIncrVacuum(pBt); VdbeBranchTaken(rc==SQLITE_DONE,2); if( rc==SQLITE_DONE ){ - pc = pOp->p2 - 1; rc = SQLITE_OK; + goto jump_to_p2; } break; } @@ -76112,13 +76860,29 @@ case OP_VBegin: { #endif /* SQLITE_OMIT_VIRTUALTABLE */ #ifndef SQLITE_OMIT_VIRTUALTABLE -/* Opcode: VCreate P1 * * P4 * +/* Opcode: VCreate P1 P2 * * * ** -** P4 is the name of a virtual table in database P1. Call the xCreate method -** for that table. +** P2 is a register that holds the name of a virtual table in database +** P1. Call the xCreate method for that table. */ case OP_VCreate: { - rc = sqlite3VtabCallCreate(db, pOp->p1, pOp->p4.z, &p->zErrMsg); + Mem sMem; /* For storing the record being decoded */ + const char *zTab; /* Name of the virtual table */ + + memset(&sMem, 0, sizeof(sMem)); + sMem.db = db; + /* Because P2 is always a static string, it is impossible for the + ** sqlite3VdbeMemCopy() to fail */ + assert( (aMem[pOp->p2].flags & MEM_Str)!=0 ); + assert( (aMem[pOp->p2].flags & MEM_Static)!=0 ); + rc = sqlite3VdbeMemCopy(&sMem, &aMem[pOp->p2]); + assert( rc==SQLITE_OK ); + zTab = (const char*)sqlite3_value_text(&sMem); + assert( zTab || db->mallocFailed ); + if( zTab ){ + rc = sqlite3VtabCallCreate(db, pOp->p1, zTab, &p->zErrMsg); + } + sqlite3VdbeMemRelease(&sMem); break; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -76130,9 +76894,9 @@ case OP_VCreate: { ** of that table. */ case OP_VDestroy: { - p->inVtabMethod = 2; + db->nVDestroy++; rc = sqlite3VtabCallDestroy(db, pOp->p1, pOp->p4.z); - p->inVtabMethod = 0; + db->nVDestroy--; break; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -76148,14 +76912,17 @@ case OP_VOpen: { VdbeCursor *pCur; sqlite3_vtab_cursor *pVtabCursor; sqlite3_vtab *pVtab; - sqlite3_module *pModule; + const sqlite3_module *pModule; assert( p->bIsReader ); pCur = 0; pVtabCursor = 0; pVtab = pOp->p4.pVtab->pVtab; - pModule = (sqlite3_module *)pVtab->pModule; - assert(pVtab && pModule); + if( pVtab==0 || NEVER(pVtab->pModule==0) ){ + rc = SQLITE_LOCKED; + break; + } + pModule = pVtab->pModule; rc = pModule->xOpen(pVtab, &pVtabCursor); sqlite3VtabImportErrmsg(p, pVtab); if( SQLITE_OK==rc ){ @@ -76166,9 +76933,11 @@ case OP_VOpen: { pCur = allocateCursor(p, pOp->p1, 0, -1, 0); if( pCur ){ pCur->pVtabCursor = pVtabCursor; + pVtab->nRef++; }else{ - db->mallocFailed = 1; + assert( db->mallocFailed ); pModule->xClose(pVtabCursor); + goto no_mem; } } break; @@ -76224,27 +76993,19 @@ case OP_VFilter: { /* jump */ iQuery = (int)pQuery->u.i; /* Invoke the xFilter method */ - { - res = 0; - apArg = p->apArg; - for(i = 0; iinVtabMethod = 1; - rc = pModule->xFilter(pVtabCursor, iQuery, pOp->p4.z, nArg, apArg); - p->inVtabMethod = 0; - sqlite3VtabImportErrmsg(p, pVtab); - if( rc==SQLITE_OK ){ - res = pModule->xEof(pVtabCursor); - } - VdbeBranchTaken(res!=0,2); - if( res ){ - pc = pOp->p2 - 1; - } + res = 0; + apArg = p->apArg; + for(i = 0; ixFilter(pVtabCursor, iQuery, pOp->p4.z, nArg, apArg); + sqlite3VtabImportErrmsg(p, pVtab); + if( rc==SQLITE_OK ){ + res = pModule->xEof(pVtabCursor); } pCur->nullRow = 0; - + VdbeBranchTaken(res!=0,2); + if( res ) goto jump_to_p2; break; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -76323,9 +77084,7 @@ case OP_VNext: { /* jump */ ** data is available) and the error code returned when xColumn or ** some other method is next invoked on the save virtual table cursor. */ - p->inVtabMethod = 1; rc = pModule->xNext(pCur->pVtabCursor); - p->inVtabMethod = 0; sqlite3VtabImportErrmsg(p, pVtab); if( rc==SQLITE_OK ){ res = pModule->xEof(pCur->pVtabCursor); @@ -76333,7 +77092,7 @@ case OP_VNext: { /* jump */ VdbeBranchTaken(!res,2); if( !res ){ /* If there is data, jump to P2 */ - pc = pOp->p2 - 1; + goto jump_to_p2_and_check_for_interrupt; } goto check_for_interrupt; } @@ -76400,7 +77159,7 @@ case OP_VRename: { */ case OP_VUpdate: { sqlite3_vtab *pVtab; - sqlite3_module *pModule; + const sqlite3_module *pModule; int nArg; int i; sqlite_int64 rowid; @@ -76412,7 +77171,11 @@ case OP_VUpdate: { ); assert( p->readOnly==0 ); pVtab = pOp->p4.pVtab->pVtab; - pModule = (sqlite3_module *)pVtab->pModule; + if( pVtab==0 || NEVER(pVtab->pModule==0) ){ + rc = SQLITE_LOCKED; + break; + } + pModule = pVtab->pModule; nArg = pOp->p2; assert( pOp->p4type==P4_VTAB ); if( ALWAYS(pModule->xUpdate) ){ @@ -76452,7 +77215,8 @@ case OP_VUpdate: { ** ** Write the current number of pages in database P1 to memory cell P2. */ -case OP_Pagecount: { /* out2-prerelease */ +case OP_Pagecount: { /* out2 */ + pOut = out2Prerelease(p, pOp); pOut->u.i = sqlite3BtreeLastPage(db->aDb[pOp->p1].pBt); break; } @@ -76468,10 +77232,11 @@ case OP_Pagecount: { /* out2-prerelease */ ** ** Store the maximum page count after the change in register P2. */ -case OP_MaxPgcnt: { /* out2-prerelease */ +case OP_MaxPgcnt: { /* out2 */ unsigned int newMax; Btree *pBt; + pOut = out2Prerelease(p, pOp); pBt = db->aDb[pOp->p1].pBt; newMax = 0; if( pOp->p3 ){ @@ -76500,9 +77265,6 @@ case OP_Init: { /* jump */ char *zTrace; char *z; - if( pOp->p2 ){ - pc = pOp->p2 - 1; - } #ifndef SQLITE_OMIT_TRACE if( db->xTrace && !p->doingRerun @@ -76530,6 +77292,7 @@ case OP_Init: { /* jump */ } #endif /* SQLITE_DEBUG */ #endif /* SQLITE_OMIT_TRACE */ + if( pOp->p2 ) goto jump_to_p2; break; } @@ -76561,8 +77324,8 @@ default: { /* This is really OP_Noop and OP_Explain */ #ifdef VDBE_PROFILE { u64 endTime = sqlite3Hwtime(); - if( endTime>start ) pOp->cycles += endTime - start; - pOp->cnt++; + if( endTime>start ) pOrigOp->cycles += endTime - start; + pOrigOp->cnt++; } #endif @@ -76572,16 +77335,16 @@ default: { /* This is really OP_Noop and OP_Explain */ ** the evaluator loop. So we can leave it out when NDEBUG is defined. */ #ifndef NDEBUG - assert( pc>=-1 && pcnOp ); + assert( pOp>=&aOp[-1] && pOp<&aOp[p->nOp-1] ); #ifdef SQLITE_DEBUG if( db->flags & SQLITE_VdbeTrace ){ if( rc!=0 ) printf("rc=%d\n",rc); - if( pOp->opflags & (OPFLG_OUT2_PRERELEASE|OPFLG_OUT2) ){ - registerTrace(pOp->p2, &aMem[pOp->p2]); + if( pOrigOp->opflags & (OPFLG_OUT2) ){ + registerTrace(pOrigOp->p2, &aMem[pOrigOp->p2]); } - if( pOp->opflags & OPFLG_OUT3 ){ - registerTrace(pOp->p3, &aMem[pOp->p3]); + if( pOrigOp->opflags & OPFLG_OUT3 ){ + registerTrace(pOrigOp->p3, &aMem[pOrigOp->p3]); } } #endif /* SQLITE_DEBUG */ @@ -76596,7 +77359,7 @@ vdbe_error_halt: p->rc = rc; testcase( sqlite3GlobalConfig.xLog!=0 ); sqlite3_log(rc, "statement aborts at %d: [%s] %s", - pc, p->zSql, p->zErrMsg); + (int)(pOp - aOp), p->zSql, p->zErrMsg); sqlite3VdbeHalt(p); if( rc==SQLITE_IOERR_NOMEM ) db->mallocFailed = 1; rc = SQLITE_ERROR; @@ -76759,7 +77522,7 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){ /* ** Open a blob handle. */ -SQLITE_API int sqlite3_blob_open( +SQLITE_API int SQLITE_STDCALL sqlite3_blob_open( sqlite3* db, /* The database connection */ const char *zDb, /* The attached database containing the blob */ const char *zTable, /* The table containing the blob */ @@ -76809,12 +77572,17 @@ SQLITE_API int sqlite3_blob_open( Incrblob *pBlob = 0; #ifdef SQLITE_ENABLE_API_ARMOR - if( !sqlite3SafetyCheckOk(db) || ppBlob==0 || zTable==0 ){ + if( ppBlob==0 ){ + return SQLITE_MISUSE_BKPT; + } +#endif + *ppBlob = 0; +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) || zTable==0 ){ return SQLITE_MISUSE_BKPT; } #endif flags = !!flags; /* flags = (flags ? 1 : 0); */ - *ppBlob = 0; sqlite3_mutex_enter(db->mutex); @@ -76991,7 +77759,7 @@ blob_open_out: ** Close a blob handle that was previously created using ** sqlite3_blob_open(). */ -SQLITE_API int sqlite3_blob_close(sqlite3_blob *pBlob){ +SQLITE_API int SQLITE_STDCALL sqlite3_blob_close(sqlite3_blob *pBlob){ Incrblob *p = (Incrblob *)pBlob; int rc; sqlite3 *db; @@ -77028,7 +77796,7 @@ static int blobReadWrite( sqlite3_mutex_enter(db->mutex); v = (Vdbe*)p->pStmt; - if( n<0 || iOffset<0 || (iOffset+n)>p->nByte ){ + if( n<0 || iOffset<0 || ((sqlite3_int64)iOffset+n)>p->nByte ){ /* Request is out of range. Return a transient error. */ rc = SQLITE_ERROR; }else if( v==0 ){ @@ -77060,14 +77828,14 @@ static int blobReadWrite( /* ** Read data from a blob handle. */ -SQLITE_API int sqlite3_blob_read(sqlite3_blob *pBlob, void *z, int n, int iOffset){ +SQLITE_API int SQLITE_STDCALL sqlite3_blob_read(sqlite3_blob *pBlob, void *z, int n, int iOffset){ return blobReadWrite(pBlob, z, n, iOffset, sqlite3BtreeData); } /* ** Write data to a blob handle. */ -SQLITE_API int sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int iOffset){ +SQLITE_API int SQLITE_STDCALL sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int iOffset){ return blobReadWrite(pBlob, (void *)z, n, iOffset, sqlite3BtreePutData); } @@ -77077,7 +77845,7 @@ SQLITE_API int sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int ** The Incrblob.nByte field is fixed for the lifetime of the Incrblob ** so no mutex is required for access. */ -SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *pBlob){ +SQLITE_API int SQLITE_STDCALL sqlite3_blob_bytes(sqlite3_blob *pBlob){ Incrblob *p = (Incrblob *)pBlob; return (p && p->pStmt) ? p->nByte : 0; } @@ -77092,7 +77860,7 @@ SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *pBlob){ ** subsequent calls to sqlite3_blob_xxx() functions (except blob_close()) ** immediately return SQLITE_ABORT. */ -SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){ +SQLITE_API int SQLITE_STDCALL sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){ int rc; Incrblob *p = (Incrblob *)pBlob; sqlite3 *db; @@ -77417,6 +78185,7 @@ struct MergeEngine { ** after the thread has finished are not dire. So we don't worry about ** memory barriers and such here. */ +typedef int (*SorterCompare)(SortSubtask*,int*,const void*,int,const void*,int); struct SortSubtask { SQLiteThread *pThread; /* Background thread, if any */ int bDone; /* Set if thread is finished but not joined */ @@ -77424,10 +78193,12 @@ struct SortSubtask { UnpackedRecord *pUnpacked; /* Space to unpack a record */ SorterList list; /* List for thread to write to a PMA */ int nPMA; /* Number of PMAs currently in file */ + SorterCompare xCompare; /* Compare function to use */ SorterFile file; /* Temp file for level-0 PMAs */ SorterFile file2; /* Space for other PMAs */ }; + /* ** Main sorter structure. A single instance of this is allocated for each ** sorter cursor created by the VDBE. @@ -77454,9 +78225,13 @@ struct VdbeSorter { u8 bUseThreads; /* True to use background threads */ u8 iPrev; /* Previous thread used to flush PMA */ u8 nTask; /* Size of aTask[] array */ + u8 typeMask; SortSubtask aTask[1]; /* One or more subtasks */ }; +#define SORTER_TYPE_INTEGER 0x01 +#define SORTER_TYPE_TEXT 0x02 + /* ** An instance of the following object is used to read records out of a ** PMA, in sorted order. The next key to be read is cached in nKey/aKey. @@ -77868,32 +78643,162 @@ static int vdbePmaReaderInit( return rc; } +/* +** A version of vdbeSorterCompare() that assumes that it has already been +** determined that the first field of key1 is equal to the first field of +** key2. +*/ +static int vdbeSorterCompareTail( + SortSubtask *pTask, /* Subtask context (for pKeyInfo) */ + int *pbKey2Cached, /* True if pTask->pUnpacked is pKey2 */ + const void *pKey1, int nKey1, /* Left side of comparison */ + const void *pKey2, int nKey2 /* Right side of comparison */ +){ + UnpackedRecord *r2 = pTask->pUnpacked; + if( *pbKey2Cached==0 ){ + sqlite3VdbeRecordUnpack(pTask->pSorter->pKeyInfo, nKey2, pKey2, r2); + *pbKey2Cached = 1; + } + return sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, r2, 1); +} /* ** Compare key1 (buffer pKey1, size nKey1 bytes) with key2 (buffer pKey2, ** size nKey2 bytes). Use (pTask->pKeyInfo) for the collation sequences ** used by the comparison. Return the result of the comparison. ** -** Before returning, object (pTask->pUnpacked) is populated with the -** unpacked version of key2. Or, if pKey2 is passed a NULL pointer, then it -** is assumed that the (pTask->pUnpacked) structure already contains the -** unpacked key to use as key2. +** If IN/OUT parameter *pbKey2Cached is true when this function is called, +** it is assumed that (pTask->pUnpacked) contains the unpacked version +** of key2. If it is false, (pTask->pUnpacked) is populated with the unpacked +** version of key2 and *pbKey2Cached set to true before returning. ** ** If an OOM error is encountered, (pTask->pUnpacked->error_rc) is set ** to SQLITE_NOMEM. */ static int vdbeSorterCompare( SortSubtask *pTask, /* Subtask context (for pKeyInfo) */ + int *pbKey2Cached, /* True if pTask->pUnpacked is pKey2 */ const void *pKey1, int nKey1, /* Left side of comparison */ const void *pKey2, int nKey2 /* Right side of comparison */ ){ UnpackedRecord *r2 = pTask->pUnpacked; - if( pKey2 ){ + if( !*pbKey2Cached ){ sqlite3VdbeRecordUnpack(pTask->pSorter->pKeyInfo, nKey2, pKey2, r2); + *pbKey2Cached = 1; } return sqlite3VdbeRecordCompare(nKey1, pKey1, r2); } +/* +** A specially optimized version of vdbeSorterCompare() that assumes that +** the first field of each key is a TEXT value and that the collation +** sequence to compare them with is BINARY. +*/ +static int vdbeSorterCompareText( + SortSubtask *pTask, /* Subtask context (for pKeyInfo) */ + int *pbKey2Cached, /* True if pTask->pUnpacked is pKey2 */ + const void *pKey1, int nKey1, /* Left side of comparison */ + const void *pKey2, int nKey2 /* Right side of comparison */ +){ + const u8 * const p1 = (const u8 * const)pKey1; + const u8 * const p2 = (const u8 * const)pKey2; + const u8 * const v1 = &p1[ p1[0] ]; /* Pointer to value 1 */ + const u8 * const v2 = &p2[ p2[0] ]; /* Pointer to value 2 */ + + int n1; + int n2; + int res; + + getVarint32(&p1[1], n1); n1 = (n1 - 13) / 2; + getVarint32(&p2[1], n2); n2 = (n2 - 13) / 2; + res = memcmp(v1, v2, MIN(n1, n2)); + if( res==0 ){ + res = n1 - n2; + } + + if( res==0 ){ + if( pTask->pSorter->pKeyInfo->nField>1 ){ + res = vdbeSorterCompareTail( + pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2 + ); + } + }else{ + if( pTask->pSorter->pKeyInfo->aSortOrder[0] ){ + res = res * -1; + } + } + + return res; +} + +/* +** A specially optimized version of vdbeSorterCompare() that assumes that +** the first field of each key is an INTEGER value. +*/ +static int vdbeSorterCompareInt( + SortSubtask *pTask, /* Subtask context (for pKeyInfo) */ + int *pbKey2Cached, /* True if pTask->pUnpacked is pKey2 */ + const void *pKey1, int nKey1, /* Left side of comparison */ + const void *pKey2, int nKey2 /* Right side of comparison */ +){ + const u8 * const p1 = (const u8 * const)pKey1; + const u8 * const p2 = (const u8 * const)pKey2; + const int s1 = p1[1]; /* Left hand serial type */ + const int s2 = p2[1]; /* Right hand serial type */ + const u8 * const v1 = &p1[ p1[0] ]; /* Pointer to value 1 */ + const u8 * const v2 = &p2[ p2[0] ]; /* Pointer to value 2 */ + int res; /* Return value */ + + assert( (s1>0 && s1<7) || s1==8 || s1==9 ); + assert( (s2>0 && s2<7) || s2==8 || s2==9 ); + + if( s1>7 && s2>7 ){ + res = s1 - s2; + }else{ + if( s1==s2 ){ + if( (*v1 ^ *v2) & 0x80 ){ + /* The two values have different signs */ + res = (*v1 & 0x80) ? -1 : +1; + }else{ + /* The two values have the same sign. Compare using memcmp(). */ + static const u8 aLen[] = {0, 1, 2, 3, 4, 6, 8 }; + int i; + res = 0; + for(i=0; i7 ){ + res = +1; + }else if( s1>7 ){ + res = -1; + }else{ + res = s1 - s2; + } + assert( res!=0 ); + + if( res>0 ){ + if( *v1 & 0x80 ) res = -1; + }else{ + if( *v2 & 0x80 ) res = +1; + } + } + } + + if( res==0 ){ + if( pTask->pSorter->pKeyInfo->nField>1 ){ + res = vdbeSorterCompareTail( + pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2 + ); + } + }else if( pTask->pSorter->pKeyInfo->aSortOrder[0] ){ + res = res * -1; + } + + return res; +} + /* ** Initialize the temporary index cursor just opened as a sorter cursor. ** @@ -77961,9 +78866,13 @@ SQLITE_PRIVATE int sqlite3VdbeSorterInit( pSorter->pKeyInfo = pKeyInfo = (KeyInfo*)((u8*)pSorter + sz); memcpy(pKeyInfo, pCsr->pKeyInfo, szKeyInfo); pKeyInfo->db = 0; - if( nField && nWorker==0 ) pKeyInfo->nField = nField; + if( nField && nWorker==0 ){ + pKeyInfo->nXField += (pKeyInfo->nField - nField); + pKeyInfo->nField = nField; + } pSorter->pgsz = pgsz = sqlite3BtreeGetPageSize(db->aDb[0].pBt); pSorter->nTask = nWorker + 1; + pSorter->iPrev = nWorker-1; pSorter->bUseThreads = (pSorter->nTask>1); pSorter->db = db; for(i=0; inTask; i++){ @@ -77989,6 +78898,12 @@ SQLITE_PRIVATE int sqlite3VdbeSorterInit( if( !pSorter->list.aMemory ) rc = SQLITE_NOMEM; } } + + if( (pKeyInfo->nField+pKeyInfo->nXField)<13 + && (pKeyInfo->aColl[0]==0 || pKeyInfo->aColl[0]==db->pDfltColl) + ){ + pSorter->typeMask = SORTER_TYPE_INTEGER | SORTER_TYPE_TEXT; + } } return rc; @@ -78013,30 +78928,24 @@ static void vdbeSorterRecordFree(sqlite3 *db, SorterRecord *pRecord){ */ static void vdbeSortSubtaskCleanup(sqlite3 *db, SortSubtask *pTask){ sqlite3DbFree(db, pTask->pUnpacked); - pTask->pUnpacked = 0; #if SQLITE_MAX_WORKER_THREADS>0 /* pTask->list.aMemory can only be non-zero if it was handed memory ** from the main thread. That only occurs SQLITE_MAX_WORKER_THREADS>0 */ if( pTask->list.aMemory ){ sqlite3_free(pTask->list.aMemory); - pTask->list.aMemory = 0; }else #endif { assert( pTask->list.aMemory==0 ); vdbeSorterRecordFree(0, pTask->list.pList); } - pTask->list.pList = 0; if( pTask->file.pFd ){ sqlite3OsCloseFree(pTask->file.pFd); - pTask->file.pFd = 0; - pTask->file.iEof = 0; } if( pTask->file2.pFd ){ sqlite3OsCloseFree(pTask->file2.pFd); - pTask->file2.pFd = 0; - pTask->file2.iEof = 0; } + memset(pTask, 0, sizeof(SortSubtask)); } #ifdef SQLITE_DEBUG_SORTER_THREADS @@ -78216,6 +79125,7 @@ SQLITE_PRIVATE void sqlite3VdbeSorterReset(sqlite3 *db, VdbeSorter *pSorter){ for(i=0; inTask; i++){ SortSubtask *pTask = &pSorter->aTask[i]; vdbeSortSubtaskCleanup(db, pTask); + pTask->pSorter = pSorter; } if( pSorter->list.aMemory==0 ){ vdbeSorterRecordFree(0, pSorter->list.pList); @@ -78277,6 +79187,7 @@ static int vdbeSorterOpenTempFile( sqlite3_file **ppFd ){ int rc; + if( sqlite3FaultSim(202) ) return SQLITE_IOERR_ACCESS; rc = sqlite3OsOpenMalloc(db->pVfs, 0, ppFd, SQLITE_OPEN_TEMP_JOURNAL | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | @@ -78324,28 +79235,42 @@ static void vdbeSorterMerge( ){ SorterRecord *pFinal = 0; SorterRecord **pp = &pFinal; - void *pVal2 = p2 ? SRVAL(p2) : 0; + int bCached = 0; while( p1 && p2 ){ int res; - res = vdbeSorterCompare(pTask, SRVAL(p1), p1->nVal, pVal2, p2->nVal); + res = pTask->xCompare( + pTask, &bCached, SRVAL(p1), p1->nVal, SRVAL(p2), p2->nVal + ); + if( res<=0 ){ *pp = p1; pp = &p1->u.pNext; p1 = p1->u.pNext; - pVal2 = 0; }else{ *pp = p2; - pp = &p2->u.pNext; + pp = &p2->u.pNext; p2 = p2->u.pNext; - if( p2==0 ) break; - pVal2 = SRVAL(p2); + bCached = 0; } } *pp = p1 ? p1 : p2; *ppOut = pFinal; } +/* +** Return the SorterCompare function to compare values collected by the +** sorter object passed as the only argument. +*/ +static SorterCompare vdbeSorterGetCompare(VdbeSorter *p){ + if( p->typeMask==SORTER_TYPE_INTEGER ){ + return vdbeSorterCompareInt; + }else if( p->typeMask==SORTER_TYPE_TEXT ){ + return vdbeSorterCompareText; + } + return vdbeSorterCompare; +} + /* ** Sort the linked list of records headed at pTask->pList. Return ** SQLITE_OK if successful, or an SQLite error code (i.e. SQLITE_NOMEM) if @@ -78360,12 +79285,14 @@ static int vdbeSorterSort(SortSubtask *pTask, SorterList *pList){ rc = vdbeSortAllocUnpacked(pTask); if( rc!=SQLITE_OK ) return rc; + p = pList->pList; + pTask->xCompare = vdbeSorterGetCompare(pTask->pSorter); + aSlot = (SorterRecord **)sqlite3MallocZero(64 * sizeof(SorterRecord *)); if( !aSlot ){ return SQLITE_NOMEM; } - p = pList->pList; while( p ){ SorterRecord *pNext; if( pList->aMemory ){ @@ -78579,13 +79506,12 @@ static int vdbeMergeEngineStep( int i; /* Index of aTree[] to recalculate */ PmaReader *pReadr1; /* First PmaReader to compare */ PmaReader *pReadr2; /* Second PmaReader to compare */ - u8 *pKey2; /* To pReadr2->aKey, or 0 if record cached */ + int bCached = 0; /* Find the first two PmaReaders to compare. The one that was just ** advanced (iPrev) and the one next to it in the array. */ pReadr1 = &pMerger->aReadr[(iPrev & 0xFFFE)]; pReadr2 = &pMerger->aReadr[(iPrev | 0x0001)]; - pKey2 = pReadr2->aKey; for(i=(pMerger->nTree+iPrev)/2; i>0; i=i/2){ /* Compare pReadr1 and pReadr2. Store the result in variable iRes. */ @@ -78595,8 +79521,8 @@ static int vdbeMergeEngineStep( }else if( pReadr2->pFd==0 ){ iRes = -1; }else{ - iRes = vdbeSorterCompare(pTask, - pReadr1->aKey, pReadr1->nKey, pKey2, pReadr2->nKey + iRes = pTask->xCompare(pTask, &bCached, + pReadr1->aKey, pReadr1->nKey, pReadr2->aKey, pReadr2->nKey ); } @@ -78618,9 +79544,9 @@ static int vdbeMergeEngineStep( if( iRes<0 || (iRes==0 && pReadr1aTree[i] = (int)(pReadr1 - pMerger->aReadr); pReadr2 = &pMerger->aReadr[ pMerger->aTree[i ^ 0x0001] ]; - pKey2 = pReadr2->aKey; + bCached = 0; }else{ - if( pReadr1->pFd ) pKey2 = 0; + if( pReadr1->pFd ) bCached = 0; pMerger->aTree[i] = (int)(pReadr2 - pMerger->aReadr); pReadr1 = &pMerger->aReadr[ pMerger->aTree[i ^ 0x0001] ]; } @@ -78727,6 +79653,16 @@ SQLITE_PRIVATE int sqlite3VdbeSorterWrite( int bFlush; /* True to flush contents of memory to PMA */ int nReq; /* Bytes of memory required */ int nPMA; /* Bytes of PMA space required */ + int t; /* serial type of first record field */ + + getVarint32((const u8*)&pVal->z[1], t); + if( t>0 && t<10 && t!=7 ){ + pSorter->typeMask &= SORTER_TYPE_INTEGER; + }else if( t>10 && (t & 0x01) ){ + pSorter->typeMask &= SORTER_TYPE_TEXT; + }else{ + pSorter->typeMask = 0; + } assert( pSorter ); @@ -78992,10 +79928,12 @@ static void vdbeMergeEngineCompare( }else if( p2->pFd==0 ){ iRes = i1; }else{ + SortSubtask *pTask = pMerger->pTask; + int bCached = 0; int res; - assert( pMerger->pTask->pUnpacked!=0 ); /* from vdbeSortSubtaskMain() */ - res = vdbeSorterCompare( - pMerger->pTask, p1->aKey, p1->nKey, p2->aKey, p2->nKey + assert( pTask->pUnpacked!=0 ); /* from vdbeSortSubtaskMain() */ + res = pTask->xCompare( + pTask, &bCached, p1->aKey, p1->nKey, p2->aKey, p2->nKey ); if( res<=0 ){ iRes = i1; @@ -79019,11 +79957,12 @@ static void vdbeMergeEngineCompare( #define INCRINIT_TASK 1 #define INCRINIT_ROOT 2 -/* Forward reference. -** The vdbeIncrMergeInit() and vdbePmaReaderIncrMergeInit() routines call each -** other (when building a merge tree). +/* +** Forward reference required as the vdbeIncrMergeInit() and +** vdbePmaReaderIncrInit() routines are called mutually recursively when +** building a merge tree. */ -static int vdbePmaReaderIncrMergeInit(PmaReader *pReadr, int eMode); +static int vdbePmaReaderIncrInit(PmaReader *pReadr, int eMode); /* ** Initialize the MergeEngine object passed as the second argument. Once this @@ -79070,7 +80009,7 @@ static int vdbeMergeEngineInit( ** better advantage of multi-processor hardware. */ rc = vdbePmaReaderNext(&pMerger->aReadr[nTree-i-1]); }else{ - rc = vdbePmaReaderIncrMergeInit(&pMerger->aReadr[i], INCRINIT_NORMAL); + rc = vdbePmaReaderIncrInit(&pMerger->aReadr[i], INCRINIT_NORMAL); } if( rc!=SQLITE_OK ) return rc; } @@ -79082,17 +80021,15 @@ static int vdbeMergeEngineInit( } /* -** Initialize the IncrMerge field of a PmaReader. -** -** If the PmaReader passed as the first argument is not an incremental-reader -** (if pReadr->pIncr==0), then this function is a no-op. Otherwise, it serves -** to open and/or initialize the temp file related fields of the IncrMerge +** The PmaReader passed as the first argument is guaranteed to be an +** incremental-reader (pReadr->pIncr!=0). This function serves to open +** and/or initialize the temp file related fields of the IncrMerge ** object at (pReadr->pIncr). ** ** If argument eMode is set to INCRINIT_NORMAL, then all PmaReaders -** in the sub-tree headed by pReadr are also initialized. Data is then loaded -** into the buffers belonging to pReadr and it is set to -** point to the first key in its range. +** in the sub-tree headed by pReadr are also initialized. Data is then +** loaded into the buffers belonging to pReadr and it is set to point to +** the first key in its range. ** ** If argument eMode is set to INCRINIT_TASK, then pReadr is guaranteed ** to be a multi-threaded PmaReader and this function is being called in a @@ -79119,59 +80056,62 @@ static int vdbeMergeEngineInit( static int vdbePmaReaderIncrMergeInit(PmaReader *pReadr, int eMode){ int rc = SQLITE_OK; IncrMerger *pIncr = pReadr->pIncr; + SortSubtask *pTask = pIncr->pTask; + sqlite3 *db = pTask->pSorter->db; /* eMode is always INCRINIT_NORMAL in single-threaded mode */ assert( SQLITE_MAX_WORKER_THREADS>0 || eMode==INCRINIT_NORMAL ); - if( pIncr ){ - SortSubtask *pTask = pIncr->pTask; - sqlite3 *db = pTask->pSorter->db; + rc = vdbeMergeEngineInit(pTask, pIncr->pMerger, eMode); - rc = vdbeMergeEngineInit(pTask, pIncr->pMerger, eMode); - - /* Set up the required files for pIncr. A multi-theaded IncrMerge object - ** requires two temp files to itself, whereas a single-threaded object - ** only requires a region of pTask->file2. */ - if( rc==SQLITE_OK ){ - int mxSz = pIncr->mxSz; + /* Set up the required files for pIncr. A multi-theaded IncrMerge object + ** requires two temp files to itself, whereas a single-threaded object + ** only requires a region of pTask->file2. */ + if( rc==SQLITE_OK ){ + int mxSz = pIncr->mxSz; #if SQLITE_MAX_WORKER_THREADS>0 - if( pIncr->bUseThread ){ - rc = vdbeSorterOpenTempFile(db, mxSz, &pIncr->aFile[0].pFd); - if( rc==SQLITE_OK ){ - rc = vdbeSorterOpenTempFile(db, mxSz, &pIncr->aFile[1].pFd); - } - }else + if( pIncr->bUseThread ){ + rc = vdbeSorterOpenTempFile(db, mxSz, &pIncr->aFile[0].pFd); + if( rc==SQLITE_OK ){ + rc = vdbeSorterOpenTempFile(db, mxSz, &pIncr->aFile[1].pFd); + } + }else #endif - /*if( !pIncr->bUseThread )*/{ - if( pTask->file2.pFd==0 ){ - assert( pTask->file2.iEof>0 ); - rc = vdbeSorterOpenTempFile(db, pTask->file2.iEof, &pTask->file2.pFd); - pTask->file2.iEof = 0; - } - if( rc==SQLITE_OK ){ - pIncr->aFile[1].pFd = pTask->file2.pFd; - pIncr->iStartOff = pTask->file2.iEof; - pTask->file2.iEof += mxSz; - } + /*if( !pIncr->bUseThread )*/{ + if( pTask->file2.pFd==0 ){ + assert( pTask->file2.iEof>0 ); + rc = vdbeSorterOpenTempFile(db, pTask->file2.iEof, &pTask->file2.pFd); + pTask->file2.iEof = 0; + } + if( rc==SQLITE_OK ){ + pIncr->aFile[1].pFd = pTask->file2.pFd; + pIncr->iStartOff = pTask->file2.iEof; + pTask->file2.iEof += mxSz; } } + } #if SQLITE_MAX_WORKER_THREADS>0 - if( rc==SQLITE_OK && pIncr->bUseThread ){ - /* Use the current thread to populate aFile[1], even though this - ** PmaReader is multi-threaded. The reason being that this function - ** is already running in background thread pIncr->pTask->thread. */ - assert( eMode==INCRINIT_ROOT || eMode==INCRINIT_TASK ); - rc = vdbeIncrPopulate(pIncr); - } + if( rc==SQLITE_OK && pIncr->bUseThread ){ + /* Use the current thread to populate aFile[1], even though this + ** PmaReader is multi-threaded. If this is an INCRINIT_TASK object, + ** then this function is already running in background thread + ** pIncr->pTask->thread. + ** + ** If this is the INCRINIT_ROOT object, then it is running in the + ** main VDBE thread. But that is Ok, as that thread cannot return + ** control to the VDBE or proceed with anything useful until the + ** first results are ready from this merger object anyway. + */ + assert( eMode==INCRINIT_ROOT || eMode==INCRINIT_TASK ); + rc = vdbeIncrPopulate(pIncr); + } #endif - if( rc==SQLITE_OK - && (SQLITE_MAX_WORKER_THREADS==0 || eMode!=INCRINIT_TASK) - ){ - rc = vdbePmaReaderNext(pReadr); - } + if( rc==SQLITE_OK && (SQLITE_MAX_WORKER_THREADS==0 || eMode!=INCRINIT_TASK) ){ + rc = vdbePmaReaderNext(pReadr); } + return rc; } @@ -79180,7 +80120,7 @@ static int vdbePmaReaderIncrMergeInit(PmaReader *pReadr, int eMode){ ** The main routine for vdbePmaReaderIncrMergeInit() operations run in ** background threads. */ -static void *vdbePmaReaderBgInit(void *pCtx){ +static void *vdbePmaReaderBgIncrInit(void *pCtx){ PmaReader *pReader = (PmaReader*)pCtx; void *pRet = SQLITE_INT_TO_PTR( vdbePmaReaderIncrMergeInit(pReader,INCRINIT_TASK) @@ -79188,20 +80128,36 @@ static void *vdbePmaReaderBgInit(void *pCtx){ pReader->pIncr->pTask->bDone = 1; return pRet; } +#endif /* -** Use a background thread to invoke vdbePmaReaderIncrMergeInit(INCRINIT_TASK) -** on the PmaReader object passed as the first argument. -** -** This call will initialize the various fields of the pReadr->pIncr -** structure and, if it is a multi-threaded IncrMerger, launch a -** background thread to populate aFile[1]. +** If the PmaReader passed as the first argument is not an incremental-reader +** (if pReadr->pIncr==0), then this function is a no-op. Otherwise, it invokes +** the vdbePmaReaderIncrMergeInit() function with the parameters passed to +** this routine to initialize the incremental merge. +** +** If the IncrMerger object is multi-threaded (IncrMerger.bUseThread==1), +** then a background thread is launched to call vdbePmaReaderIncrMergeInit(). +** Or, if the IncrMerger is single threaded, the same function is called +** using the current thread. */ -static int vdbePmaReaderBgIncrInit(PmaReader *pReadr){ - void *pCtx = (void*)pReadr; - return vdbeSorterCreateThread(pReadr->pIncr->pTask, vdbePmaReaderBgInit, pCtx); -} +static int vdbePmaReaderIncrInit(PmaReader *pReadr, int eMode){ + IncrMerger *pIncr = pReadr->pIncr; /* Incremental merger */ + int rc = SQLITE_OK; /* Return code */ + if( pIncr ){ +#if SQLITE_MAX_WORKER_THREADS>0 + assert( pIncr->bUseThread==0 || eMode==INCRINIT_TASK ); + if( pIncr->bUseThread ){ + void *pCtx = (void*)pReadr; + rc = vdbeSorterCreateThread(pIncr->pTask, vdbePmaReaderBgIncrInit, pCtx); + }else #endif + { + rc = vdbePmaReaderIncrMergeInit(pReadr, eMode); + } + } + return rc; +} /* ** Allocate a new MergeEngine object to merge the contents of nPMA level-0 @@ -79413,6 +80369,11 @@ static int vdbeSorterSetupMerge(VdbeSorter *pSorter){ MergeEngine *pMain = 0; #if SQLITE_MAX_WORKER_THREADS sqlite3 *db = pTask0->pSorter->db; + int i; + SorterCompare xCompare = vdbeSorterGetCompare(pSorter); + for(i=0; inTask; i++){ + pSorter->aTask[i].xCompare = xCompare; + } #endif rc = vdbeSorterMergeTreeBuild(pSorter, &pMain); @@ -79441,15 +80402,21 @@ static int vdbeSorterSetupMerge(VdbeSorter *pSorter){ } } for(iTask=0; rc==SQLITE_OK && iTasknTask; iTask++){ + /* Check that: + ** + ** a) The incremental merge object is configured to use the + ** right task, and + ** b) If it is using task (nTask-1), it is configured to run + ** in single-threaded mode. This is important, as the + ** root merge (INCRINIT_ROOT) will be using the same task + ** object. + */ PmaReader *p = &pMain->aReadr[iTask]; - assert( p->pIncr==0 || p->pIncr->pTask==&pSorter->aTask[iTask] ); - if( p->pIncr ){ - if( iTask==pSorter->nTask-1 ){ - rc = vdbePmaReaderIncrMergeInit(p, INCRINIT_TASK); - }else{ - rc = vdbePmaReaderBgIncrInit(p); - } - } + assert( p->pIncr==0 || ( + (p->pIncr->pTask==&pSorter->aTask[iTask]) /* a */ + && (iTask!=pSorter->nTask-1 || p->pIncr->bUseThread==0) /* b */ + )); + rc = vdbePmaReaderIncrInit(p, INCRINIT_TASK); } } pMain = 0; @@ -80404,7 +81371,7 @@ static void incrAggFunctionDepth(Expr *pExpr, int N){ ** SELECT a+b, c+d FROM t1 ORDER BY (a+b) COLLATE nocase; ** ** The nSubquery parameter specifies how many levels of subquery the -** alias is removed from the original expression. The usually value is +** alias is removed from the original expression. The usual value is ** zero but it might be more if the alias is contained within a subquery ** of the original expression. The Expr.op2 field of TK_AGG_FUNCTION ** structures must be increased by the nSubquery amount. @@ -80424,7 +81391,6 @@ static void resolveAlias( assert( iCol>=0 && iColnExpr ); pOrig = pEList->a[iCol].pExpr; assert( pOrig!=0 ); - assert( pOrig->flags & EP_Resolved ); db = pParse->db; pDup = sqlite3ExprDup(db, pOrig, 0); if( pDup==0 ) return; @@ -80572,9 +81538,10 @@ static int lookupName( testcase( pNC->ncFlags & NC_PartIdx ); testcase( pNC->ncFlags & NC_IsCheck ); if( (pNC->ncFlags & (NC_PartIdx|NC_IsCheck))!=0 ){ - /* Silently ignore database qualifiers inside CHECK constraints and partial - ** indices. Do not raise errors because that might break legacy and - ** because it does not hurt anything to just ignore the database name. */ + /* Silently ignore database qualifiers inside CHECK constraints and + ** partial indices. Do not raise errors because that might break + ** legacy and because it does not hurt anything to just ignore the + ** database name. */ zDb = 0; }else{ for(i=0; inDb; i++){ @@ -80645,7 +81612,8 @@ static int lookupName( if( pMatch ){ pExpr->iTable = pMatch->iCursor; pExpr->pTab = pMatch->pTab; - assert( (pMatch->jointype & JT_RIGHT)==0 ); /* RIGHT JOIN not (yet) supported */ + /* RIGHT JOIN not (yet) supported */ + assert( (pMatch->jointype & JT_RIGHT)==0 ); if( (pMatch->jointype & JT_LEFT)!=0 ){ ExprSetProperty(pExpr, EP_CanBeNull); } @@ -80966,7 +81934,8 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ pExpr->affinity = SQLITE_AFF_INTEGER; break; } -#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) */ +#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) + && !defined(SQLITE_OMIT_SUBQUERY) */ /* A lone identifier is the name of a column. */ @@ -81031,19 +82000,20 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ if( n==2 ){ pExpr->iTable = exprProbability(pList->a[1].pExpr); if( pExpr->iTable<0 ){ - sqlite3ErrorMsg(pParse, "second argument to likelihood() must be a " - "constant between 0.0 and 1.0"); + sqlite3ErrorMsg(pParse, + "second argument to likelihood() must be a " + "constant between 0.0 and 1.0"); pNC->nErr++; } }else{ - /* EVIDENCE-OF: R-61304-29449 The unlikely(X) function is equivalent to - ** likelihood(X, 0.0625). - ** EVIDENCE-OF: R-01283-11636 The unlikely(X) function is short-hand for - ** likelihood(X,0.0625). - ** EVIDENCE-OF: R-36850-34127 The likely(X) function is short-hand for - ** likelihood(X,0.9375). - ** EVIDENCE-OF: R-53436-40973 The likely(X) function is equivalent to - ** likelihood(X,0.9375). */ + /* EVIDENCE-OF: R-61304-29449 The unlikely(X) function is + ** equivalent to likelihood(X, 0.0625). + ** EVIDENCE-OF: R-01283-11636 The unlikely(X) function is + ** short-hand for likelihood(X,0.0625). + ** EVIDENCE-OF: R-36850-34127 The likely(X) function is short-hand + ** for likelihood(X,0.9375). + ** EVIDENCE-OF: R-53436-40973 The likely(X) function is equivalent + ** to likelihood(X,0.9375). */ /* TUNING: unlikely() probability is 0.0625. likely() is 0.9375 */ pExpr->iTable = pDef->zName[0]=='u' ? 8388608 : 125829120; } @@ -81060,7 +82030,9 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ return WRC_Prune; } #endif - if( pDef->funcFlags & SQLITE_FUNC_CONSTANT ) ExprSetProperty(pExpr,EP_Constant); + if( pDef->funcFlags & SQLITE_FUNC_CONSTANT ){ + ExprSetProperty(pExpr,EP_ConstFunc); + } } if( is_agg && (pNC->ncFlags & NC_AllowAgg)==0 ){ sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId); @@ -81312,9 +82284,11 @@ static int resolveCompoundOrderBy( if( pItem->pExpr==pE ){ pItem->pExpr = pNew; }else{ - assert( pItem->pExpr->op==TK_COLLATE ); - assert( pItem->pExpr->pLeft==pE ); - pItem->pExpr->pLeft = pNew; + Expr *pParent = pItem->pExpr; + assert( pParent->op==TK_COLLATE ); + while( pParent->pLeft->op==TK_COLLATE ) pParent = pParent->pLeft; + assert( pParent->pLeft==pE ); + pParent->pLeft = pNew; } sqlite3ExprDelete(db, pE); pItem->u.x.iOrderByCol = (u16)iCol; @@ -81371,7 +82345,8 @@ SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy( resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr); return 1; } - resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr, zType,0); + resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr, + zType,0); } } return 0; @@ -81504,6 +82479,20 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ sqlite3ResolveExprNames(&sNC, p->pOffset) ){ return WRC_Abort; } + + /* If the SF_Converted flags is set, then this Select object was + ** was created by the convertCompoundSelectToSubquery() function. + ** In this case the ORDER BY clause (p->pOrderBy) should be resolved + ** as if it were part of the sub-query, not the parent. This block + ** moves the pOrderBy down to the sub-query. It will be moved back + ** after the names have been resolved. */ + if( p->selFlags & SF_Converted ){ + Select *pSub = p->pSrc->a[0].pSelect; + assert( p->pSrc->nSrc==1 && p->pOrderBy ); + assert( pSub->pPrior && pSub->pOrderBy==0 ); + pSub->pOrderBy = p->pOrderBy; + p->pOrderBy = 0; + } /* Recursively resolve names in all subqueries */ @@ -81586,12 +82575,30 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ sNC.pNext = 0; sNC.ncFlags |= NC_AllowAgg; + /* If this is a converted compound query, move the ORDER BY clause from + ** the sub-query back to the parent query. At this point each term + ** within the ORDER BY clause has been transformed to an integer value. + ** These integers will be replaced by copies of the corresponding result + ** set expressions by the call to resolveOrderGroupBy() below. */ + if( p->selFlags & SF_Converted ){ + Select *pSub = p->pSrc->a[0].pSelect; + p->pOrderBy = pSub->pOrderBy; + pSub->pOrderBy = 0; + } + /* Process the ORDER BY clause for singleton SELECT statements. ** The ORDER BY clause for compounds SELECT statements is handled ** below, after all of the result-sets for all of the elements of ** the compound have been resolved. + ** + ** If there is an ORDER BY clause on a term of a compound-select other + ** than the right-most term, then that is a syntax error. But the error + ** is not detected until much later, and so we need to go ahead and + ** resolve those symbols on the incorrect ORDER BY for consistency. */ - if( !isCompound && resolveOrderGroupBy(&sNC, p, p->pOrderBy, "ORDER") ){ + if( isCompound<=nCompound /* Defer right-most ORDER BY of a compound */ + && resolveOrderGroupBy(&sNC, p, p->pOrderBy, "ORDER") + ){ return WRC_Abort; } if( db->mallocFailed ){ @@ -81861,10 +82868,11 @@ SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr){ SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken( Parse *pParse, /* Parsing context */ Expr *pExpr, /* Add the "COLLATE" clause to this expression */ - const Token *pCollName /* Name of collating sequence */ + const Token *pCollName, /* Name of collating sequence */ + int dequote /* True to dequote pCollName */ ){ if( pCollName->n>0 ){ - Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLLATE, pCollName, 1); + Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLLATE, pCollName, dequote); if( pNew ){ pNew->pLeft = pExpr; pNew->flags |= EP_Collate|EP_Skip; @@ -81878,7 +82886,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse *pParse, Expr *pExpr, con assert( zC!=0 ); s.z = zC; s.n = sqlite3Strlen30(s.z); - return sqlite3ExprAddCollateToken(pParse, pExpr, &s); + return sqlite3ExprAddCollateToken(pParse, pExpr, &s, 0); } /* @@ -81924,9 +82932,9 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){ pColl = sqlite3GetCollSeq(pParse, ENC(db), 0, p->u.zToken); break; } - if( p->pTab!=0 - && (op==TK_AGG_COLUMN || op==TK_COLUMN + if( (op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_REGISTER || op==TK_TRIGGER) + && p->pTab!=0 ){ /* op==TK_REGISTER && p->pTab!=0 happens when pExpr was originally ** a TK_COLUMN but was previously evaluated and cached in a register */ @@ -81938,10 +82946,25 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){ break; } if( p->flags & EP_Collate ){ - if( ALWAYS(p->pLeft) && (p->pLeft->flags & EP_Collate)!=0 ){ + if( p->pLeft && (p->pLeft->flags & EP_Collate)!=0 ){ p = p->pLeft; }else{ - p = p->pRight; + Expr *pNext = p->pRight; + /* The Expr.x union is never used at the same time as Expr.pRight */ + assert( p->x.pList==0 || p->pRight==0 ); + /* p->flags holds EP_Collate and p->pLeft->flags does not. And + ** p->x.pSelect cannot. So if p->x.pLeft exists, it must hold at + ** least one EP_Collate. Thus the following two ALWAYS. */ + if( p->x.pList!=0 && ALWAYS(!ExprHasProperty(p, EP_xIsSelect)) ){ + int i; + for(i=0; ALWAYS(ix.pList->nExpr); i++){ + if( ExprHasProperty(p->x.pList->a[i].pExpr, EP_Collate) ){ + pNext = p->x.pList->a[i].pExpr; + break; + } + } + } + p = pNext; } }else{ break; @@ -82147,6 +83170,9 @@ static void heightOfSelect(Select *p, int *pnHeight){ ** Expr.pSelect member has a height of 1. Any other expression ** has a height equal to the maximum height of any other ** referenced Expr plus one. +** +** Also propagate EP_Propagate flags up from Expr.x.pList to Expr.flags, +** if appropriate. */ static void exprSetHeight(Expr *p){ int nHeight = 0; @@ -82154,8 +83180,9 @@ static void exprSetHeight(Expr *p){ heightOfExpr(p->pRight, &nHeight); if( ExprHasProperty(p, EP_xIsSelect) ){ heightOfSelect(p->x.pSelect, &nHeight); - }else{ + }else if( p->x.pList ){ heightOfExprList(p->x.pList, &nHeight); + p->flags |= EP_Propagate & sqlite3ExprListFlags(p->x.pList); } p->nHeight = nHeight + 1; } @@ -82164,8 +83191,12 @@ static void exprSetHeight(Expr *p){ ** Set the Expr.nHeight variable using the exprSetHeight() function. If ** the height is greater than the maximum allowed expression depth, ** leave an error in pParse. +** +** Also propagate all EP_Propagate flags from the Expr.x.pList into +** Expr.flags. */ -SQLITE_PRIVATE void sqlite3ExprSetHeight(Parse *pParse, Expr *p){ +SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){ + if( pParse->nErr ) return; exprSetHeight(p); sqlite3ExprCheckHeight(pParse, p->nHeight); } @@ -82179,8 +83210,17 @@ SQLITE_PRIVATE int sqlite3SelectExprHeight(Select *p){ heightOfSelect(p, &nHeight); return nHeight; } -#else - #define exprSetHeight(y) +#else /* ABOVE: Height enforcement enabled. BELOW: Height enforcement off */ +/* +** Propagate all EP_Propagate flags from the Expr.x.pList into +** Expr.flags. +*/ +SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){ + if( p && p->x.pList && !ExprHasProperty(p, EP_xIsSelect) ){ + p->flags |= EP_Propagate & sqlite3ExprListFlags(p->x.pList); + } +} +#define exprSetHeight(y) #endif /* SQLITE_MAX_EXPR_DEPTH>0 */ /* @@ -82282,11 +83322,11 @@ SQLITE_PRIVATE void sqlite3ExprAttachSubtrees( }else{ if( pRight ){ pRoot->pRight = pRight; - pRoot->flags |= EP_Collate & pRight->flags; + pRoot->flags |= EP_Propagate & pRight->flags; } if( pLeft ){ pRoot->pLeft = pLeft; - pRoot->flags |= EP_Collate & pLeft->flags; + pRoot->flags |= EP_Propagate & pLeft->flags; } exprSetHeight(pRoot); } @@ -82386,7 +83426,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse *pParse, ExprList *pList, Token * } pNew->x.pList = pList; assert( !ExprHasProperty(pNew, EP_xIsSelect) ); - sqlite3ExprSetHeight(pParse, pNew); + sqlite3ExprSetHeightAndFlags(pParse, pNew); return pNew; } @@ -83001,6 +84041,22 @@ SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){ sqlite3DbFree(db, pList); } +/* +** Return the bitwise-OR of all Expr.flags fields in the given +** ExprList. +*/ +SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList *pList){ + int i; + u32 m = 0; + if( pList ){ + for(i=0; inExpr; i++){ + Expr *pExpr = pList->a[i].pExpr; + if( ALWAYS(pExpr) ) m |= pExpr->flags; + } + } + return m; +} + /* ** These routines are Walker callbacks used to check expressions to ** see if they are "constant" for some definition of constant. The @@ -83041,7 +84097,7 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ ** and either pWalker->eCode==4 or 5 or the function has the ** SQLITE_FUNC_CONST flag. */ case TK_FUNCTION: - if( pWalker->eCode>=4 || ExprHasProperty(pExpr,EP_Constant) ){ + if( pWalker->eCode>=4 || ExprHasProperty(pExpr,EP_ConstFunc) ){ return WRC_Continue; }else{ pWalker->eCode = 0; @@ -83435,7 +84491,7 @@ SQLITE_PRIVATE int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int ** ephemeral table. */ p = (ExprHasProperty(pX, EP_xIsSelect) ? pX->x.pSelect : 0); - if( ALWAYS(pParse->nErr==0) && isCandidateForInOpt(p) ){ + if( pParse->nErr==0 && isCandidateForInOpt(p) ){ sqlite3 *db = pParse->db; /* Database connection */ Table *pTab; /* Table . */ Expr *pExpr; /* Expression */ @@ -83760,6 +84816,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect( pSel->pLimit = sqlite3PExpr(pParse, TK_INTEGER, 0, 0, &sqlite3IntTokens[1]); pSel->iLimit = 0; + pSel->selFlags &= ~SF_MultiValue; if( sqlite3Select(pParse, pSel, &dest) ){ return 0; } @@ -84048,7 +85105,8 @@ SQLITE_PRIVATE void sqlite3ExprCacheStore(Parse *pParse, int iTab, int iCol, int int idxLru; struct yColCache *p; - assert( iReg>0 ); /* Register numbers are always positive */ + /* Unless an error has occurred, register numbers are always positive. */ + assert( iReg>0 || pParse->nErr || pParse->db->mallocFailed ); assert( iCol>=-1 && iCol<32768 ); /* Finite column numbers */ /* The SQLITE_ColumnCache flag disables the column cache. This is used @@ -85124,7 +86182,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m break; } case TK_ID: { - sqlite3TreeViewLine(pView,"ID %Q", pExpr->u.zToken); + sqlite3TreeViewLine(pView,"ID \"%w\"", pExpr->u.zToken); break; } #ifndef SQLITE_OMIT_CAST @@ -85759,7 +86817,7 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Expr *pA, Expr *pB, int iTab){ if( sqlite3ExprCompare(pA->pLeft, pB->pLeft, iTab) ) return 2; if( sqlite3ExprCompare(pA->pRight, pB->pRight, iTab) ) return 2; if( sqlite3ExprListCompare(pA->x.pList, pB->x.pList, iTab) ) return 2; - if( ALWAYS((combinedFlags & EP_Reduced)==0) ){ + if( ALWAYS((combinedFlags & EP_Reduced)==0) && pA->op!=TK_STRING ){ if( pA->iColumn!=pB->iColumn ) return 2; if( pA->iTable!=pB->iTable && (pA->iTable!=iTab || NEVER(pB->iTable>=0)) ) return 2; @@ -86291,6 +87349,7 @@ static void renameParentFunc( n = sqlite3GetToken(z, &token); }while( token==TK_SPACE ); + if( token==TK_ILLEGAL ) break; zParent = sqlite3DbStrNDup(db, (const char *)z, n); if( zParent==0 ) break; sqlite3Dequote(zParent); @@ -86855,7 +87914,10 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ */ if( pDflt ){ sqlite3_value *pVal = 0; - if( sqlite3ValueFromExpr(db, pDflt, SQLITE_UTF8, SQLITE_AFF_NONE, &pVal) ){ + int rc; + rc = sqlite3ValueFromExpr(db, pDflt, SQLITE_UTF8, SQLITE_AFF_NONE, &pVal); + assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); + if( rc!=SQLITE_OK ){ db->mallocFailed = 1; return; } @@ -88514,14 +89576,17 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){ z = argv[2]; if( pIndex ){ + tRowcnt *aiRowEst = 0; int nCol = pIndex->nKeyCol+1; #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 - tRowcnt * const aiRowEst = pIndex->aiRowEst = (tRowcnt*)sqlite3MallocZero( - sizeof(tRowcnt) * nCol - ); - if( aiRowEst==0 ) pInfo->db->mallocFailed = 1; -#else - tRowcnt * const aiRowEst = 0; + /* Index.aiRowEst may already be set here if there are duplicate + ** sqlite_stat1 entries for this index. In that case just clobber + ** the old data with the new instead of allocating a new array. */ + if( pIndex->aiRowEst==0 ){ + pIndex->aiRowEst = (tRowcnt*)sqlite3MallocZero(sizeof(tRowcnt) * nCol); + if( pIndex->aiRowEst==0 ) pInfo->db->mallocFailed = 1; + } + aiRowEst = pIndex->aiRowEst; #endif pIndex->bUnordered = 0; decodeIntArray((char*)z, nCol, aiRowEst, pIndex->aiRowLogEst, pIndex); @@ -89077,7 +90142,7 @@ static void attachFunc( case SQLITE_NULL: /* No key specified. Use the key from the main database */ sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey); - if( nKey>0 || sqlite3BtreeGetReserve(db->aDb[0].pBt)>0 ){ + if( nKey>0 || sqlite3BtreeGetOptimalReserve(db->aDb[0].pBt)>0 ){ rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); } break; @@ -89184,7 +90249,7 @@ static void detachFunc( sqlite3BtreeClose(pDb->pBt); pDb->pBt = 0; pDb->pSchema = 0; - sqlite3ResetAllSchemasOfConnection(db); + sqlite3CollapseDatabaseArray(db); return; detach_error: @@ -89218,7 +90283,6 @@ static void codeAttach( SQLITE_OK!=(rc = resolveAttachExpr(&sName, pDbname)) || SQLITE_OK!=(rc = resolveAttachExpr(&sName, pKey)) ){ - pParse->nErr++; goto attach_end; } @@ -89540,7 +90604,7 @@ SQLITE_PRIVATE int sqlite3FixTriggerStep( ** Setting the auth function to NULL disables this hook. The default ** setting of the auth function is NULL. */ -SQLITE_API int sqlite3_set_authorizer( +SQLITE_API int SQLITE_STDCALL sqlite3_set_authorizer( sqlite3 *db, int (*xAuth)(void*,int,const char*,const char*,const char*,const char*), void *pArg @@ -89877,9 +90941,11 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ assert( pParse->pToplevel==0 ); db = pParse->db; - if( db->mallocFailed ) return; if( pParse->nested ) return; - if( pParse->nErr ) return; + if( db->mallocFailed || pParse->nErr ){ + if( pParse->rc==SQLITE_OK ) pParse->rc = SQLITE_ERROR; + return; + } /* Begin by generating some termination code at the end of the ** vdbe program @@ -89961,7 +91027,7 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ /* Get the VDBE program ready for execution */ - if( v && ALWAYS(pParse->nErr==0) && !db->mallocFailed ){ + if( v && pParse->nErr==0 && !db->mallocFailed ){ assert( pParse->iCacheLevel==0 ); /* Disables and re-enables match */ /* A minimum of one cursor is required if autoincrement is used * See ticket [a696379c1f08866] */ @@ -90043,10 +91109,6 @@ SQLITE_PRIVATE Table *sqlite3FindTable(sqlite3 *db, const char *zName, const cha Table *p = 0; int i; -#ifdef SQLITE_ENABLE_API_ARMOR - if( !sqlite3SafetyCheckOk(db) || zName==0 ) return 0; -#endif - /* All mutexes are required for schema access. Make sure we hold them. */ assert( zDatabase!=0 || sqlite3BtreeHoldsAllMutexes(db) ); #if SQLITE_USER_AUTHENTICATION @@ -90500,14 +91562,12 @@ SQLITE_PRIVATE int sqlite3TwoPartName( if( ALWAYS(pName2!=0) && pName2->n>0 ){ if( db->init.busy ) { sqlite3ErrorMsg(pParse, "corrupt database"); - pParse->nErr++; return -1; } *pUnqual = pName2; iDb = sqlite3FindDb(db, pName1); if( iDb<0 ){ sqlite3ErrorMsg(pParse, "unknown database %T", pName1); - pParse->nErr++; return -1; } }else{ @@ -90666,7 +91726,7 @@ SQLITE_PRIVATE void sqlite3StartTable( if( !noErr ){ sqlite3ErrorMsg(pParse, "table %T already exists", pName); }else{ - assert( !db->init.busy ); + assert( !db->init.busy || CORRUPT_DB ); sqlite3CodeVerifySchema(pParse, iDb); } goto begin_table_error; @@ -90955,7 +92015,8 @@ SQLITE_PRIVATE void sqlite3AddColumnType(Parse *pParse, Token *pType){ p = pParse->pNewTable; if( p==0 || NEVER(p->nCol<1) ) return; pCol = &p->aCol[p->nCol-1]; - assert( pCol->zType==0 ); + assert( pCol->zType==0 || CORRUPT_DB ); + sqlite3DbFree(pParse->db, pCol->zType); pCol->zType = sqlite3NameFromToken(pParse->db, pType); pCol->affinity = sqlite3AffinityType(pCol->zType, &pCol->szEst); } @@ -91466,11 +92527,14 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ assert( pPk!=0 ); nPk = pPk->nKeyCol; - /* Make sure every column of the PRIMARY KEY is NOT NULL */ - for(i=0; iaCol[pPk->aiColumn[i]].notNull = 1; + /* Make sure every column of the PRIMARY KEY is NOT NULL. (Except, + ** do not enforce this for imposter tables.) */ + if( !db->init.imposterTable ){ + for(i=0; iaCol[pPk->aiColumn[i]].notNull = 1; + } + pPk->uniqNotNull = 1; } - pPk->uniqNotNull = 1; /* The root page of the PRIMARY KEY is the table root page */ pPk->tnum = pTab->tnum; @@ -92186,6 +93250,7 @@ SQLITE_PRIVATE void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, } assert( pParse->nErr==0 ); assert( pName->nSrc==1 ); + if( sqlite3ReadSchema(pParse) ) goto exit_drop_table; if( noErr ) db->suppressErr++; pTab = sqlite3LocateTableItem(pParse, isView, &pName->a[0]); if( noErr ) db->suppressErr--; @@ -92499,7 +93564,8 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ addr2 = sqlite3VdbeCurrentAddr(v); } sqlite3VdbeAddOp3(v, OP_SorterData, iSorter, regRecord, iIdx); - sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, 1); + sqlite3VdbeAddOp3(v, OP_Last, iIdx, 0, -1); + sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, 0); sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); sqlite3ReleaseTempReg(pParse, regRecord); sqlite3VdbeAddOp2(v, OP_SorterNext, iSorter, addr2); VdbeCoverage(v); @@ -92592,8 +93658,7 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex( char *zExtra = 0; /* Extra space after the Index object */ Index *pPk = 0; /* PRIMARY KEY index for WITHOUT ROWID tables */ - assert( pParse->nErr==0 ); /* Never called with prior errors */ - if( db->mallocFailed || IN_DECLARE_VTAB ){ + if( db->mallocFailed || IN_DECLARE_VTAB || pParse->nErr>0 ){ goto exit_create_index; } if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ @@ -92919,6 +93984,7 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex( pIdx->onError = pIndex->onError; } } + pRet = pIdx; goto exit_create_index; } } @@ -93511,7 +94577,6 @@ SQLITE_PRIVATE void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pI SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(SrcList *p){ if( p ){ int i; - assert( p->a || p->nSrc==0 ); for(i=p->nSrc-1; i>0; i--){ p->a[i].jointype = p->a[i-1].jointype; } @@ -93758,8 +94823,7 @@ SQLITE_PRIVATE void sqlite3UniqueConstraint( StrAccum errMsg; Table *pTab = pIdx->pTable; - sqlite3StrAccumInit(&errMsg, 0, 0, 200); - errMsg.db = pParse->db; + sqlite3StrAccumInit(&errMsg, pParse->db, 0, 0, 200); for(j=0; jnKeyCol; j++){ char *zCol = pTab->aCol[pIdx->aiColumn[j]].zName; if( j ) sqlite3StrAccumAppend(&errMsg, ", ", 2); @@ -94705,7 +95769,7 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere( pInClause->x.pSelect = pSelect; pInClause->flags |= EP_xIsSelect; - sqlite3ExprSetHeight(pParse, pInClause); + sqlite3ExprSetHeightAndFlags(pParse, pInClause); return pInClause; /* something went wrong. clean up anything allocated. */ @@ -95378,7 +96442,9 @@ SQLITE_PRIVATE void sqlite3ResolvePartIdxLabel(Parse *pParse, int iLabel){ ** Return the collating function associated with a function. */ static CollSeq *sqlite3GetFuncCollSeq(sqlite3_context *context){ - VdbeOp *pOp = &context->pVdbe->aOp[context->iOp-1]; + VdbeOp *pOp; + assert( context->pVdbe!=0 ); + pOp = &context->pVdbe->aOp[context->iOp-1]; assert( pOp->opcode==OP_CollSeq ); assert( pOp->p4type==P4_COLLSEQ ); return pOp->p4.pColl; @@ -95586,13 +96652,13 @@ static void printfFunc( StrAccum str; const char *zFormat; int n; + sqlite3 *db = sqlite3_context_db_handle(context); if( argc>=1 && (zFormat = (const char*)sqlite3_value_text(argv[0]))!=0 ){ x.nArg = argc-1; x.nUsed = 0; x.apArg = argv+1; - sqlite3StrAccumInit(&str, 0, 0, SQLITE_MAX_LENGTH); - str.db = sqlite3_context_db_handle(context); + sqlite3StrAccumInit(&str, db, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]); sqlite3XPrintf(&str, SQLITE_PRINTF_SQLFUNC, zFormat, &x); n = str.nChar; sqlite3_result_text(context, sqlite3StrAccumFinish(&str), n, @@ -95647,6 +96713,14 @@ static void substrFunc( } } } +#ifdef SQLITE_SUBSTR_COMPATIBILITY + /* If SUBSTR_COMPATIBILITY is defined then substr(X,0,N) work the same as + ** as substr(X,1,N) - it returns the first N characters of X. This + ** is essentially a back-out of the bug-fix in check-in [5fc125d362df4b8] + ** from 2009-02-02 for compatibility of applications that exploited the + ** old buggy behavior. */ + if( p1==0 ) p1 = 1; /* */ +#endif if( argc==3 ){ p2 = sqlite3_value_int(argv[2]); if( p2<0 ){ @@ -95734,7 +96808,7 @@ static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ #endif /* -** Allocate nByte bytes of space using sqlite3_malloc(). If the +** Allocate nByte bytes of space using sqlite3Malloc(). If the ** allocation fails, call sqlite3_result_error_nomem() to notify ** the database handle that malloc() has failed and return NULL. ** If nByte is larger than the maximum string or blob length, then @@ -96108,7 +97182,7 @@ static int patternCompare( /* ** The sqlite3_strglob() interface. */ -SQLITE_API int sqlite3_strglob(const char *zGlobPattern, const char *zString){ +SQLITE_API int SQLITE_STDCALL sqlite3_strglob(const char *zGlobPattern, const char *zString){ return patternCompare((u8*)zGlobPattern, (u8*)zString, &globInfo, 0)==0; } @@ -96403,7 +97477,7 @@ static void charFunc( ){ unsigned char *z, *zOut; int i; - zOut = z = sqlite3_malloc( argc*4+1 ); + zOut = z = sqlite3_malloc64( argc*4+1 ); if( z==0 ){ sqlite3_result_error_nomem(context); return; @@ -96551,7 +97625,7 @@ static void replaceFunc( return; } zOld = zOut; - zOut = sqlite3_realloc(zOut, (int)nOut); + zOut = sqlite3_realloc64(zOut, (int)nOut); if( zOut==0 ){ sqlite3_result_error_nomem(context); sqlite3_free(zOld); @@ -96913,8 +97987,7 @@ static void groupConcatStep( if( pAccum ){ sqlite3 *db = sqlite3_context_db_handle(context); - int firstTerm = pAccum->useMalloc==0; - pAccum->useMalloc = 2; + int firstTerm = pAccum->mxAlloc==0; pAccum->mxAlloc = db->aLimit[SQLITE_LIMIT_LENGTH]; if( !firstTerm ){ if( argc==2 ){ @@ -96998,6 +98071,11 @@ SQLITE_PRIVATE void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive) ** then set aWc[0] through aWc[2] to the wildcard characters and ** return TRUE. If the function is not a LIKE-style function then ** return FALSE. +** +** *pIsNocase is set to true if uppercase and lowercase are equivalent for +** the function (default for LIKE). If the function makes the distinction +** between uppercase and lowercase (as does GLOB) then *pIsNocase is set to +** false. */ SQLITE_PRIVATE int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){ FuncDef *pDef; @@ -98329,7 +99407,8 @@ static Trigger *fkActionTrigger( iFromCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom; assert( iFromCol>=0 ); - tToCol.z = pIdx ? pTab->aCol[pIdx->aiColumn[i]].zName : "oid"; + assert( pIdx!=0 || (pTab->iPKey>=0 && pTab->iPKeynCol) ); + tToCol.z = pTab->aCol[pIdx ? pIdx->aiColumn[i] : pTab->iPKey].zName; tFromCol.z = pFKey->pFrom->aCol[iFromCol].zName; tToCol.n = sqlite3Strlen30(tToCol.z); @@ -98341,10 +99420,10 @@ static Trigger *fkActionTrigger( ** parent table are used for the comparison. */ pEq = sqlite3PExpr(pParse, TK_EQ, sqlite3PExpr(pParse, TK_DOT, - sqlite3PExpr(pParse, TK_ID, 0, 0, &tOld), - sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol) + sqlite3ExprAlloc(db, TK_ID, &tOld, 0), + sqlite3ExprAlloc(db, TK_ID, &tToCol, 0) , 0), - sqlite3PExpr(pParse, TK_ID, 0, 0, &tFromCol) + sqlite3ExprAlloc(db, TK_ID, &tFromCol, 0) , 0); pWhere = sqlite3ExprAnd(db, pWhere, pEq); @@ -98356,12 +99435,12 @@ static Trigger *fkActionTrigger( if( pChanges ){ pEq = sqlite3PExpr(pParse, TK_IS, sqlite3PExpr(pParse, TK_DOT, - sqlite3PExpr(pParse, TK_ID, 0, 0, &tOld), - sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol), + sqlite3ExprAlloc(db, TK_ID, &tOld, 0), + sqlite3ExprAlloc(db, TK_ID, &tToCol, 0), 0), sqlite3PExpr(pParse, TK_DOT, - sqlite3PExpr(pParse, TK_ID, 0, 0, &tNew), - sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol), + sqlite3ExprAlloc(db, TK_ID, &tNew, 0), + sqlite3ExprAlloc(db, TK_ID, &tToCol, 0), 0), 0); pWhen = sqlite3ExprAnd(db, pWhen, pEq); @@ -98371,8 +99450,8 @@ static Trigger *fkActionTrigger( Expr *pNew; if( action==OE_Cascade ){ pNew = sqlite3PExpr(pParse, TK_DOT, - sqlite3PExpr(pParse, TK_ID, 0, 0, &tNew), - sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol) + sqlite3ExprAlloc(db, TK_ID, &tNew, 0), + sqlite3ExprAlloc(db, TK_ID, &tToCol, 0) , 0); }else if( action==OE_SetDflt ){ Expr *pDflt = pFKey->pFrom->aCol[iFromCol].pDflt; @@ -98419,13 +99498,12 @@ static Trigger *fkActionTrigger( pTrigger = (Trigger *)sqlite3DbMallocZero(db, sizeof(Trigger) + /* struct Trigger */ sizeof(TriggerStep) + /* Single step in trigger program */ - nFrom + 1 /* Space for pStep->target.z */ + nFrom + 1 /* Space for pStep->zTarget */ ); if( pTrigger ){ pStep = pTrigger->step_list = (TriggerStep *)&pTrigger[1]; - pStep->target.z = (char *)&pStep[1]; - pStep->target.n = nFrom; - memcpy((char *)pStep->target.z, zFrom, nFrom); + pStep->zTarget = (char *)&pStep[1]; + memcpy((char *)pStep->zTarget, zFrom, nFrom); pStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE); pStep->pExprList = sqlite3ExprListDup(db, pList, EXPRDUP_REDUCE); @@ -98890,20 +99968,23 @@ static int xferOptimization( /* ** This routine is called to handle SQL of the following forms: ** -** insert into TABLE (IDLIST) values(EXPRLIST) +** insert into TABLE (IDLIST) values(EXPRLIST),(EXPRLIST),... ** insert into TABLE (IDLIST) select +** insert into TABLE (IDLIST) default values ** ** The IDLIST following the table name is always optional. If omitted, -** then a list of all columns for the table is substituted. The IDLIST -** appears in the pColumn parameter. pColumn is NULL if IDLIST is omitted. +** then a list of all (non-hidden) columns for the table is substituted. +** The IDLIST appears in the pColumn parameter. pColumn is NULL if IDLIST +** is omitted. ** -** The pList parameter holds EXPRLIST in the first form of the INSERT -** statement above, and pSelect is NULL. For the second form, pList is -** NULL and pSelect is a pointer to the select statement used to generate -** data for the insert. +** For the pSelect parameter holds the values to be inserted for the +** first two forms shown above. A VALUES clause is really just short-hand +** for a SELECT statement that omits the FROM clause and everything else +** that follows. If the pSelect parameter is NULL, that means that the +** DEFAULT VALUES form of the INSERT statement is intended. ** ** The code generated follows one of four templates. For a simple -** insert with data coming from a VALUES clause, the code executes +** insert with data coming from a single-row VALUES clause, the code executes ** once straight down through. Pseudo-code follows (we call this ** the "1st template"): ** @@ -99010,7 +100091,7 @@ SQLITE_PRIVATE void sqlite3Insert( u8 useTempTable = 0; /* Store SELECT results in intermediate table */ u8 appendFlag = 0; /* True if the insert is likely to be an append */ u8 withoutRowid; /* 0 for normal table. 1 for WITHOUT ROWID table */ - u8 bIdListInOrder = 1; /* True if IDLIST is in table order */ + u8 bIdListInOrder; /* True if IDLIST is in table order */ ExprList *pList = 0; /* List of VALUES() to be inserted */ /* Register allocations */ @@ -99035,8 +100116,8 @@ SQLITE_PRIVATE void sqlite3Insert( } /* If the Select object is really just a simple VALUES() list with a - ** single row values (the common case) then keep that one row of values - ** and go ahead and discard the Select object + ** single row (the common case) then keep that one row of values + ** and discard the other (unused) parts of the pSelect object */ if( pSelect && (pSelect->selFlags & SF_Values)!=0 && pSelect->pPrior==0 ){ pList = pSelect->pEList; @@ -99144,6 +100225,7 @@ SQLITE_PRIVATE void sqlite3Insert( ** is appears in the original table. (The index of the INTEGER ** PRIMARY KEY in the original table is pTab->iPKey.) */ + bIdListInOrder = (pTab->tabFlags & TF_OOOHidden)==0; if( pColumn ){ for(i=0; inId; i++){ pColumn->a[i].idx = -1; @@ -99179,7 +100261,8 @@ SQLITE_PRIVATE void sqlite3Insert( ** co-routine is the common header to the 3rd and 4th templates. */ if( pSelect ){ - /* Data is coming from a SELECT. Generate a co-routine to run the SELECT */ + /* Data is coming from a SELECT or from a multi-row VALUES clause. + ** Generate a co-routine to run the SELECT. */ int regYield; /* Register holding co-routine entry-point */ int addrTop; /* Top of the co-routine */ int rc; /* Result code */ @@ -99192,8 +100275,7 @@ SQLITE_PRIVATE void sqlite3Insert( dest.nSdst = pTab->nCol; rc = sqlite3Select(pParse, pSelect, &dest); regFromSelect = dest.iSdst; - assert( pParse->nErr==0 || rc ); - if( rc || db->mallocFailed ) goto insert_cleanup; + if( rc || db->mallocFailed || pParse->nErr ) goto insert_cleanup; sqlite3VdbeAddOp1(v, OP_EndCoroutine, regYield); sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */ assert( pSelect->pEList ); @@ -99241,8 +100323,8 @@ SQLITE_PRIVATE void sqlite3Insert( sqlite3ReleaseTempReg(pParse, regTempRowid); } }else{ - /* This is the case if the data for the INSERT is coming from a VALUES - ** clause + /* This is the case if the data for the INSERT is coming from a + ** single-row VALUES clause */ NameContext sNC; memset(&sNC, 0, sizeof(sNC)); @@ -100313,6 +101395,7 @@ static int xferOptimization( int onError, /* How to handle constraint errors */ int iDbDest /* The database of pDest */ ){ + sqlite3 *db = pParse->db; ExprList *pEList; /* The result set of the SELECT */ Table *pSrc; /* The table in the FROM clause of SELECT */ Index *pSrcIdx, *pDestIdx; /* Source and destination indices */ @@ -100460,11 +101543,11 @@ static int xferOptimization( ** the extra complication to make this rule less restrictive is probably ** not worth the effort. Ticket [6284df89debdfa61db8073e062908af0c9b6118e] */ - if( (pParse->db->flags & SQLITE_ForeignKeys)!=0 && pDest->pFKey!=0 ){ + if( (db->flags & SQLITE_ForeignKeys)!=0 && pDest->pFKey!=0 ){ return 0; } #endif - if( (pParse->db->flags & SQLITE_CountRows)!=0 ){ + if( (db->flags & SQLITE_CountRows)!=0 ){ return 0; /* xfer opt does not play well with PRAGMA count_changes */ } @@ -100475,7 +101558,7 @@ static int xferOptimization( #ifdef SQLITE_TEST sqlite3_xferopt_count++; #endif - iDbSrc = sqlite3SchemaToIndex(pParse->db, pSrc->pSchema); + iDbSrc = sqlite3SchemaToIndex(db, pSrc->pSchema); v = sqlite3GetVdbe(pParse); sqlite3CodeVerifySchema(pParse, iDbSrc); iSrc = pParse->nTab++; @@ -100485,14 +101568,18 @@ static int xferOptimization( regRowid = sqlite3GetTempReg(pParse); sqlite3OpenTable(pParse, iDest, iDbDest, pDest, OP_OpenWrite); assert( HasRowid(pDest) || destHasUniqueIdx ); - if( (pDest->iPKey<0 && pDest->pIndex!=0) /* (1) */ + if( (db->flags & SQLITE_Vacuum)==0 && ( + (pDest->iPKey<0 && pDest->pIndex!=0) /* (1) */ || destHasUniqueIdx /* (2) */ || (onError!=OE_Abort && onError!=OE_Rollback) /* (3) */ - ){ + )){ /* In some circumstances, we are able to run the xfer optimization - ** only if the destination table is initially empty. This code makes - ** that determination. Conditions under which the destination must - ** be empty: + ** only if the destination table is initially empty. Unless the + ** SQLITE_Vacuum flag is set, this block generates code to make + ** that determination. If SQLITE_Vacuum is set, then the destination + ** table is always empty. + ** + ** Conditions under which the destination must be empty: ** ** (1) There is no INTEGER PRIMARY KEY but there are indices. ** (If the destination is not initially empty, the rowid fields @@ -100535,6 +101622,7 @@ static int xferOptimization( sqlite3TableLock(pParse, iDbSrc, pSrc->tnum, 0, pSrc->zName); } for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){ + u8 useSeekResult = 0; for(pSrcIdx=pSrc->pIndex; ALWAYS(pSrcIdx); pSrcIdx=pSrcIdx->pNext){ if( xferCompatibleIndex(pDestIdx, pSrcIdx) ) break; } @@ -100548,7 +101636,33 @@ static int xferOptimization( VdbeComment((v, "%s", pDestIdx->zName)); addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_RowKey, iSrc, regData); + if( db->flags & SQLITE_Vacuum ){ + /* This INSERT command is part of a VACUUM operation, which guarantees + ** that the destination table is empty. If all indexed columns use + ** collation sequence BINARY, then it can also be assumed that the + ** index will be populated by inserting keys in strictly sorted + ** order. In this case, instead of seeking within the b-tree as part + ** of every OP_IdxInsert opcode, an OP_Last is added before the + ** OP_IdxInsert to seek to the point within the b-tree where each key + ** should be inserted. This is faster. + ** + ** If any of the indexed columns use a collation sequence other than + ** BINARY, this optimization is disabled. This is because the user + ** might change the definition of a collation sequence and then run + ** a VACUUM command. In that case keys may not be written in strictly + ** sorted order. */ + for(i=0; inColumn; i++){ + char *zColl = pSrcIdx->azColl[i]; + assert( zColl!=0 ); + if( sqlite3_stricmp("BINARY", zColl) ) break; + } + if( i==pSrcIdx->nColumn ){ + useSeekResult = OPFLAG_USESEEKRESULT; + sqlite3VdbeAddOp3(v, OP_Last, iDest, 0, -1); + } + } sqlite3VdbeAddOp3(v, OP_IdxInsert, iDest, regData, 1); + sqlite3VdbeChangeP5(v, useSeekResult); sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1); VdbeCoverage(v); sqlite3VdbeJumpHere(v, addr1); sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0); @@ -100598,7 +101712,7 @@ static int xferOptimization( ** argument to xCallback(). If xCallback=NULL then no callback ** is invoked, even for queries. */ -SQLITE_API int sqlite3_exec( +SQLITE_API int SQLITE_STDCALL sqlite3_exec( sqlite3 *db, /* The database on which the SQL executes */ const char *zSql, /* The SQL to be executed */ sqlite3_callback xCallback, /* Invoke this callback routine */ @@ -101667,7 +102781,7 @@ static int sqlite3LoadExtension( const char *zEntry; char *zAltEntry = 0; void **aHandle; - int nMsg = 300 + sqlite3Strlen30(zFile); + u64 nMsg = 300 + sqlite3Strlen30(zFile); int ii; /* Shared library endings to try if zFile cannot be loaded as written */ @@ -101710,7 +102824,7 @@ static int sqlite3LoadExtension( #endif if( handle==0 ){ if( pzErrMsg ){ - *pzErrMsg = zErrmsg = sqlite3_malloc(nMsg); + *pzErrMsg = zErrmsg = sqlite3_malloc64(nMsg); if( zErrmsg ){ sqlite3_snprintf(nMsg, zErrmsg, "unable to open shared library [%s]", zFile); @@ -101736,7 +102850,7 @@ static int sqlite3LoadExtension( if( xInit==0 && zProc==0 ){ int iFile, iEntry, c; int ncFile = sqlite3Strlen30(zFile); - zAltEntry = sqlite3_malloc(ncFile+30); + zAltEntry = sqlite3_malloc64(ncFile+30); if( zAltEntry==0 ){ sqlite3OsDlClose(pVfs, handle); return SQLITE_NOMEM; @@ -101758,7 +102872,7 @@ static int sqlite3LoadExtension( if( xInit==0 ){ if( pzErrMsg ){ nMsg += sqlite3Strlen30(zEntry); - *pzErrMsg = zErrmsg = sqlite3_malloc(nMsg); + *pzErrMsg = zErrmsg = sqlite3_malloc64(nMsg); if( zErrmsg ){ sqlite3_snprintf(nMsg, zErrmsg, "no entry point [%s] in shared library [%s]", zEntry, zFile); @@ -101793,7 +102907,7 @@ static int sqlite3LoadExtension( db->aExtension[db->nExtension++] = handle; return SQLITE_OK; } -SQLITE_API int sqlite3_load_extension( +SQLITE_API int SQLITE_STDCALL sqlite3_load_extension( sqlite3 *db, /* Load the extension into this database connection */ const char *zFile, /* Name of the shared library containing extension */ const char *zProc, /* Entry point. Use "sqlite3_extension_init" if 0 */ @@ -101824,7 +102938,7 @@ SQLITE_PRIVATE void sqlite3CloseExtensions(sqlite3 *db){ ** Enable or disable extension loading. Extension loading is disabled by ** default so as not to open security holes in older applications. */ -SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff){ +SQLITE_API int SQLITE_STDCALL sqlite3_enable_load_extension(sqlite3 *db, int onoff){ sqlite3_mutex_enter(db->mutex); if( onoff ){ db->flags |= SQLITE_LoadExtension; @@ -101857,7 +102971,7 @@ static const sqlite3_api_routines sqlite3Apis = { 0 }; */ typedef struct sqlite3AutoExtList sqlite3AutoExtList; static SQLITE_WSD struct sqlite3AutoExtList { - int nExt; /* Number of entries in aExt[] */ + u32 nExt; /* Number of entries in aExt[] */ void (**aExt)(void); /* Pointers to the extension init functions */ } sqlite3Autoext = { 0, 0 }; @@ -101881,7 +102995,7 @@ static SQLITE_WSD struct sqlite3AutoExtList { ** Register a statically linked extension that is automatically ** loaded by every new database connection. */ -SQLITE_API int sqlite3_auto_extension(void (*xInit)(void)){ +SQLITE_API int SQLITE_STDCALL sqlite3_auto_extension(void (*xInit)(void)){ int rc = SQLITE_OK; #ifndef SQLITE_OMIT_AUTOINIT rc = sqlite3_initialize(); @@ -101890,7 +103004,7 @@ SQLITE_API int sqlite3_auto_extension(void (*xInit)(void)){ }else #endif { - int i; + u32 i; #if SQLITE_THREADSAFE sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); #endif @@ -101900,9 +103014,9 @@ SQLITE_API int sqlite3_auto_extension(void (*xInit)(void)){ if( wsdAutoext.aExt[i]==xInit ) break; } if( i==wsdAutoext.nExt ){ - int nByte = (wsdAutoext.nExt+1)*sizeof(wsdAutoext.aExt[0]); + u64 nByte = (wsdAutoext.nExt+1)*sizeof(wsdAutoext.aExt[0]); void (**aNew)(void); - aNew = sqlite3_realloc(wsdAutoext.aExt, nByte); + aNew = sqlite3_realloc64(wsdAutoext.aExt, nByte); if( aNew==0 ){ rc = SQLITE_NOMEM; }else{ @@ -101926,7 +103040,7 @@ SQLITE_API int sqlite3_auto_extension(void (*xInit)(void)){ ** Return 1 if xInit was found on the list and removed. Return 0 if xInit ** was not on the list. */ -SQLITE_API int sqlite3_cancel_auto_extension(void (*xInit)(void)){ +SQLITE_API int SQLITE_STDCALL sqlite3_cancel_auto_extension(void (*xInit)(void)){ #if SQLITE_THREADSAFE sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); #endif @@ -101934,7 +103048,7 @@ SQLITE_API int sqlite3_cancel_auto_extension(void (*xInit)(void)){ int n = 0; wsdAutoextInit; sqlite3_mutex_enter(mutex); - for(i=wsdAutoext.nExt-1; i>=0; i--){ + for(i=(int)wsdAutoext.nExt-1; i>=0; i--){ if( wsdAutoext.aExt[i]==xInit ){ wsdAutoext.nExt--; wsdAutoext.aExt[i] = wsdAutoext.aExt[wsdAutoext.nExt]; @@ -101949,7 +103063,7 @@ SQLITE_API int sqlite3_cancel_auto_extension(void (*xInit)(void)){ /* ** Reset the automatic extension loading mechanism. */ -SQLITE_API void sqlite3_reset_auto_extension(void){ +SQLITE_API void SQLITE_STDCALL sqlite3_reset_auto_extension(void){ #ifndef SQLITE_OMIT_AUTOINIT if( sqlite3_initialize()==SQLITE_OK ) #endif @@ -101972,7 +103086,7 @@ SQLITE_API void sqlite3_reset_auto_extension(void){ ** If anything goes wrong, set an error in the database connection. */ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){ - int i; + u32 i; int go = 1; int rc; int (*xInit)(sqlite3*,char**,const sqlite3_api_routines*); @@ -102031,11 +103145,18 @@ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){ #endif /*************************************************************************** -** The next block of code, including the PragTyp_XXXX macro definitions and -** the aPragmaName[] object is composed of generated code. DO NOT EDIT. -** -** To add new pragmas, edit the code in ../tool/mkpragmatab.tcl and rerun -** that script. Then copy/paste the output in place of the following: +** The "pragma.h" include file is an automatically generated file that +** that includes the PragType_XXXX macro definitions and the aPragmaName[] +** object. This ensures that the aPragmaName[] table is arranged in +** lexicographical order to facility a binary search of the pragma name. +** Do not edit pragma.h directly. Edit and rerun the script in at +** ../tool/mkpragmatab.tcl. */ +/************** Include pragma.h in the middle of pragma.c *******************/ +/************** Begin file pragma.h ******************************************/ +/* DO NOT EDIT! +** This file is automatically generated by the script at +** ../tool/mkpragmatab.tcl. To update the set of pragmas, edit +** that script and rerun it. */ #define PragTyp_HEADER_VALUE 0 #define PragTyp_AUTO_VACUUM 1 @@ -102270,6 +103391,10 @@ static const struct sPragmaNames { /* ePragTyp: */ PragTyp_INDEX_LIST, /* ePragFlag: */ PragFlag_NeedSchema, /* iArg: */ 0 }, + { /* zName: */ "index_xinfo", + /* ePragTyp: */ PragTyp_INDEX_INFO, + /* ePragFlag: */ PragFlag_NeedSchema, + /* iArg: */ 1 }, #endif #if !defined(SQLITE_OMIT_INTEGRITY_CHECK) { /* zName: */ "integrity_check", @@ -102486,9 +103611,10 @@ static const struct sPragmaNames { /* iArg: */ SQLITE_WriteSchema|SQLITE_RecoveryMode }, #endif }; -/* Number of pragmas: 58 on by default, 71 total. */ -/* End of the automatically generated pragma table. -***************************************************************************/ +/* Number of pragmas: 59 on by default, 72 total. */ + +/************** End of pragma.h **********************************************/ +/************** Continuing where we left off in pragma.c *********************/ /* ** Interpret the given string as a safety level. Return 0 for OFF, @@ -102624,15 +103750,15 @@ static int changeTempStorage(Parse *pParse, const char *zStorageType){ */ static void returnSingleInt(Parse *pParse, const char *zLabel, i64 value){ Vdbe *v = sqlite3GetVdbe(pParse); - int mem = ++pParse->nMem; + int nMem = ++pParse->nMem; i64 *pI64 = sqlite3DbMallocRaw(pParse->db, sizeof(value)); if( pI64 ){ memcpy(pI64, &value, sizeof(value)); } - sqlite3VdbeAddOp4(v, OP_Int64, 0, mem, 0, (char*)pI64, P4_INT64); + sqlite3VdbeAddOp4(v, OP_Int64, 0, nMem, 0, (char*)pI64, P4_INT64); sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLabel, SQLITE_STATIC); - sqlite3VdbeAddOp2(v, OP_ResultRow, mem, 1); + sqlite3VdbeAddOp2(v, OP_ResultRow, nMem, 1); } @@ -102741,6 +103867,7 @@ SQLITE_PRIVATE void sqlite3Pragma( sqlite3 *db = pParse->db; /* The database connection */ Db *pDb; /* The specific database being pragmaed */ Vdbe *v = sqlite3GetVdbe(pParse); /* Prepared statement */ + const struct sPragmaNames *pPragma; if( v==0 ) return; sqlite3VdbeRunOnlyOnce(v); @@ -102776,6 +103903,17 @@ SQLITE_PRIVATE void sqlite3Pragma( /* Send an SQLITE_FCNTL_PRAGMA file-control to the underlying VFS ** connection. If it returns SQLITE_OK, then assume that the VFS ** handled the pragma and generate a no-op prepared statement. + ** + ** IMPLEMENTATION-OF: R-12238-55120 Whenever a PRAGMA statement is parsed, + ** an SQLITE_FCNTL_PRAGMA file control is sent to the open sqlite3_file + ** object corresponding to the database file to which the pragma + ** statement refers. + ** + ** IMPLEMENTATION-OF: R-29875-31678 The argument to the SQLITE_FCNTL_PRAGMA + ** file control is an array of pointers to strings (char**) in which the + ** second element of the array is the name of the pragma and the third + ** element is the argument to the pragma or NULL if the pragma has no + ** argument. */ aFcntl[0] = 0; aFcntl[1] = zLeft; @@ -102785,11 +103923,11 @@ SQLITE_PRIVATE void sqlite3Pragma( rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_PRAGMA, (void*)aFcntl); if( rc==SQLITE_OK ){ if( aFcntl[0] ){ - int mem = ++pParse->nMem; - sqlite3VdbeAddOp4(v, OP_String8, 0, mem, 0, aFcntl[0], 0); + int nMem = ++pParse->nMem; + sqlite3VdbeAddOp4(v, OP_String8, 0, nMem, 0, aFcntl[0], 0); sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "result", SQLITE_STATIC); - sqlite3VdbeAddOp2(v, OP_ResultRow, mem, 1); + sqlite3VdbeAddOp2(v, OP_ResultRow, nMem, 1); sqlite3_free(aFcntl[0]); } goto pragma_out; @@ -102818,14 +103956,15 @@ SQLITE_PRIVATE void sqlite3Pragma( } } if( lwr>upr ) goto pragma_out; + pPragma = &aPragmaNames[mid]; /* Make sure the database schema is loaded if the pragma requires that */ - if( (aPragmaNames[mid].mPragFlag & PragFlag_NeedSchema)!=0 ){ + if( (pPragma->mPragFlag & PragFlag_NeedSchema)!=0 ){ if( sqlite3ReadSchema(pParse) ) goto pragma_out; } /* Jump to the appropriate pragma handler */ - switch( aPragmaNames[mid].ePragTyp ){ + switch( pPragma->ePragTyp ){ #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED) /* @@ -103393,7 +104532,9 @@ SQLITE_PRIVATE void sqlite3Pragma( sqlite3ErrorMsg(pParse, "Safety level may not be changed inside a transaction"); }else{ - pDb->safety_level = getSafetyLevel(zRight,0,1)+1; + int iLevel = (getSafetyLevel(zRight,0,1)+1) & PAGER_SYNCHRONOUS_MASK; + if( iLevel==0 ) iLevel = 1; + pDb->safety_level = iLevel; setAllPagerFlags(db); } } @@ -103404,10 +104545,9 @@ SQLITE_PRIVATE void sqlite3Pragma( #ifndef SQLITE_OMIT_FLAG_PRAGMAS case PragTyp_FLAG: { if( zRight==0 ){ - returnSingleInt(pParse, aPragmaNames[mid].zName, - (db->flags & aPragmaNames[mid].iArg)!=0 ); + returnSingleInt(pParse, pPragma->zName, (db->flags & pPragma->iArg)!=0 ); }else{ - int mask = aPragmaNames[mid].iArg; /* Mask of bits to set or clear. */ + int mask = pPragma->iArg; /* Mask of bits to set or clear. */ if( db->autoCommit==0 ){ /* Foreign key support may not be enabled or disabled while not ** in auto-commit mode. */ @@ -103489,7 +104629,7 @@ SQLITE_PRIVATE void sqlite3Pragma( }else if( pPk==0 ){ k = 1; }else{ - for(k=1; ALWAYS(k<=pTab->nCol) && pPk->aiColumn[k-1]!=i; k++){} + for(k=1; k<=pTab->nCol && pPk->aiColumn[k-1]!=i; k++){} } sqlite3VdbeAddOp2(v, OP_Integer, k, 6); sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6); @@ -103536,20 +104676,42 @@ SQLITE_PRIVATE void sqlite3Pragma( pIdx = sqlite3FindIndex(db, zRight, zDb); if( pIdx ){ int i; + int mx; + if( pPragma->iArg ){ + /* PRAGMA index_xinfo (newer version with more rows and columns) */ + mx = pIdx->nColumn; + pParse->nMem = 6; + }else{ + /* PRAGMA index_info (legacy version) */ + mx = pIdx->nKeyCol; + pParse->nMem = 3; + } pTab = pIdx->pTable; - sqlite3VdbeSetNumCols(v, 3); - pParse->nMem = 3; + sqlite3VdbeSetNumCols(v, pParse->nMem); sqlite3CodeVerifySchema(pParse, iDb); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seqno", SQLITE_STATIC); sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "cid", SQLITE_STATIC); sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "name", SQLITE_STATIC); - for(i=0; inKeyCol; i++){ + if( pPragma->iArg ){ + sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "desc", SQLITE_STATIC); + sqlite3VdbeSetColName(v, 4, COLNAME_NAME, "coll", SQLITE_STATIC); + sqlite3VdbeSetColName(v, 5, COLNAME_NAME, "key", SQLITE_STATIC); + } + for(i=0; iaiColumn[i]; sqlite3VdbeAddOp2(v, OP_Integer, i, 1); sqlite3VdbeAddOp2(v, OP_Integer, cnum, 2); - assert( pTab->nCol>cnum ); - sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, pTab->aCol[cnum].zName, 0); - sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3); + if( cnum<0 ){ + sqlite3VdbeAddOp2(v, OP_Null, 0, 3); + }else{ + sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, pTab->aCol[cnum].zName, 0); + } + if( pPragma->iArg ){ + sqlite3VdbeAddOp2(v, OP_Integer, pIdx->aSortOrder[i], 4); + sqlite3VdbeAddOp4(v, OP_String8, 0, 5, 0, pIdx->azColl[i], 0); + sqlite3VdbeAddOp2(v, OP_Integer, inKeyCol, 6); + } + sqlite3VdbeAddOp2(v, OP_ResultRow, 1, pParse->nMem); } } } @@ -103562,17 +104724,22 @@ SQLITE_PRIVATE void sqlite3Pragma( pTab = sqlite3FindTable(db, zRight, zDb); if( pTab ){ v = sqlite3GetVdbe(pParse); - sqlite3VdbeSetNumCols(v, 3); - pParse->nMem = 3; + sqlite3VdbeSetNumCols(v, 5); + pParse->nMem = 5; sqlite3CodeVerifySchema(pParse, iDb); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", SQLITE_STATIC); sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC); sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "unique", SQLITE_STATIC); + sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "origin", SQLITE_STATIC); + sqlite3VdbeSetColName(v, 4, COLNAME_NAME, "partial", SQLITE_STATIC); for(pIdx=pTab->pIndex, i=0; pIdx; pIdx=pIdx->pNext, i++){ + const char *azOrigin[] = { "c", "u", "pk" }; sqlite3VdbeAddOp2(v, OP_Integer, i, 1); sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, pIdx->zName, 0); sqlite3VdbeAddOp2(v, OP_Integer, IsUniqueIndex(pIdx), 3); - sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3); + sqlite3VdbeAddOp4(v, OP_String8, 0, 4, 0, azOrigin[pIdx->idxType], 0); + sqlite3VdbeAddOp2(v, OP_Integer, pIdx->pPartIdxWhere!=0, 5); + sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 5); } } } @@ -104142,9 +105309,9 @@ SQLITE_PRIVATE void sqlite3Pragma( ** applications for any purpose. */ case PragTyp_HEADER_VALUE: { - int iCookie = aPragmaNames[mid].iArg; /* Which cookie to read or write */ + int iCookie = pPragma->iArg; /* Which cookie to read or write */ sqlite3VdbeUsesBtree(v, iDb); - if( zRight && (aPragmaNames[mid].mPragFlag & PragFlag_ReadOnly)==0 ){ + if( zRight && (pPragma->mPragFlag & PragFlag_ReadOnly)==0 ){ /* Write the specified cookie value */ static const VdbeOpList setCookie[] = { { OP_Transaction, 0, 1, 0}, /* 0 */ @@ -104246,8 +105413,9 @@ SQLITE_PRIVATE void sqlite3Pragma( /* ** PRAGMA shrink_memory ** - ** This pragma attempts to free as much memory as possible from the - ** current database connection. + ** IMPLEMENTATION-OF: R-23445-46109 This pragma causes the database + ** connection on which it is invoked to free up as much memory as it + ** can, by calling sqlite3_db_release_memory(). */ case PragTyp_SHRINK_MEMORY: { sqlite3_db_release_memory(db); @@ -104264,7 +105432,7 @@ SQLITE_PRIVATE void sqlite3Pragma( ** disables the timeout. */ /*case PragTyp_BUSY_TIMEOUT*/ default: { - assert( aPragmaNames[mid].ePragTyp==PragTyp_BUSY_TIMEOUT ); + assert( pPragma->ePragTyp==PragTyp_BUSY_TIMEOUT ); if( zRight ){ sqlite3_busy_timeout(db, sqlite3Atoi(zRight)); } @@ -104276,8 +105444,12 @@ SQLITE_PRIVATE void sqlite3Pragma( ** PRAGMA soft_heap_limit ** PRAGMA soft_heap_limit = N ** - ** Call sqlite3_soft_heap_limit64(N). Return the result. If N is omitted, - ** use -1. + ** IMPLEMENTATION-OF: R-26343-45930 This pragma invokes the + ** sqlite3_soft_heap_limit64() interface with the argument N, if N is + ** specified and is a non-negative integer. + ** IMPLEMENTATION-OF: R-64451-07163 The soft_heap_limit pragma always + ** returns the same integer that would be returned by the + ** sqlite3_soft_heap_limit64(-1) C-language function. */ case PragTyp_SOFT_HEAP_LIMIT: { sqlite3_int64 N; @@ -104463,7 +105635,7 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */ if( argv[1]==0 ){ corruptSchema(pData, argv[0], 0); - }else if( argv[2] && argv[2][0] ){ + }else if( sqlite3_strnicmp(argv[2],"create ",7)==0 ){ /* Call the parser to process a CREATE TABLE, INDEX or VIEW. ** But because db->init.busy is set to 1, no VDBE code is generated ** or executed. All the parser does is build the internal data @@ -104494,8 +105666,8 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char } } sqlite3_finalize(pStmt); - }else if( argv[0]==0 ){ - corruptSchema(pData, 0, 0); + }else if( argv[0]==0 || (argv[2]!=0 && argv[2][0]!=0) ){ + corruptSchema(pData, argv[0], 0); }else{ /* If the SQL column is blank it means this is an index that ** was created to be the PRIMARY KEY or to fulfill a UNIQUE @@ -105173,7 +106345,7 @@ SQLITE_PRIVATE int sqlite3Reprepare(Vdbe *p){ ** and the statement is automatically recompiled if an schema change ** occurs. */ -SQLITE_API int sqlite3_prepare( +SQLITE_API int SQLITE_STDCALL sqlite3_prepare( sqlite3 *db, /* Database handle. */ const char *zSql, /* UTF-8 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ @@ -105185,7 +106357,7 @@ SQLITE_API int sqlite3_prepare( assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 ); /* VERIFY: F13021 */ return rc; } -SQLITE_API int sqlite3_prepare_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_prepare_v2( sqlite3 *db, /* Database handle. */ const char *zSql, /* UTF-8 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ @@ -105261,7 +106433,7 @@ static int sqlite3Prepare16( ** and the statement is automatically recompiled if an schema change ** occurs. */ -SQLITE_API int sqlite3_prepare16( +SQLITE_API int SQLITE_STDCALL sqlite3_prepare16( sqlite3 *db, /* Database handle. */ const void *zSql, /* UTF-16 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ @@ -105273,7 +106445,7 @@ SQLITE_API int sqlite3_prepare16( assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 ); /* VERIFY: F13021 */ return rc; } -SQLITE_API int sqlite3_prepare16_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_prepare16_v2( sqlite3 *db, /* Database handle. */ const void *zSql, /* UTF-16 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ @@ -105402,7 +106574,6 @@ SQLITE_PRIVATE Select *sqlite3SelectNew( Select standin; sqlite3 *db = pParse->db; pNew = sqlite3DbMallocZero(db, sizeof(*pNew) ); - assert( db->mallocFailed || !pOffset || pLimit ); /* OFFSET implies LIMIT */ if( pNew==0 ){ assert( db->mallocFailed ); pNew = &standin; @@ -105422,7 +106593,7 @@ SQLITE_PRIVATE Select *sqlite3SelectNew( pNew->op = TK_SELECT; pNew->pLimit = pLimit; pNew->pOffset = pOffset; - assert( pOffset==0 || pLimit!=0 ); + assert( pOffset==0 || pLimit!=0 || pParse->nErr>0 || db->mallocFailed!=0 ); pNew->addrOpenEphm[0] = -1; pNew->addrOpenEphm[1] = -1; if( db->mallocFailed ) { @@ -105854,20 +107025,17 @@ static void pushOntoSorter( } sqlite3VdbeAddOp2(v, op, pSort->iECursor, regRecord); if( pSelect->iLimit ){ - int addr1, addr2; + int addr; int iLimit; if( pSelect->iOffset ){ iLimit = pSelect->iOffset+1; }else{ iLimit = pSelect->iLimit; } - addr1 = sqlite3VdbeAddOp1(v, OP_IfZero, iLimit); VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_AddImm, iLimit, -1); - addr2 = sqlite3VdbeAddOp0(v, OP_Goto); - sqlite3VdbeJumpHere(v, addr1); + addr = sqlite3VdbeAddOp3(v, OP_IfNotZero, iLimit, 0, -1); VdbeCoverage(v); sqlite3VdbeAddOp1(v, OP_Last, pSort->iECursor); sqlite3VdbeAddOp1(v, OP_Delete, pSort->iECursor); - sqlite3VdbeJumpHere(v, addr2); + sqlite3VdbeJumpHere(v, addr); } } @@ -106264,7 +107432,7 @@ static void selectInnerLoop( ** the output for us. */ if( pSort==0 && p->iLimit ){ - sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1); VdbeCoverage(v); + sqlite3VdbeAddOp2(v, OP_DecrJumpZero, p->iLimit, iBreak); VdbeCoverage(v); } } @@ -106675,7 +107843,7 @@ static const char *columnTypeImpl( ** of the SELECT statement. Return the declaration type and origin ** data for the result-set column of the sub-select. */ - if( iCol>=0 && ALWAYS(iColpEList->nExpr) ){ + if( iCol>=0 && iColpEList->nExpr ){ /* If iCol is less than zero, then the expression requests the ** rowid of the sub-select or view. This expression is legal (see ** test case misc2.2.2) - it always evaluates to NULL. @@ -106995,12 +108163,14 @@ static void selectAddColumnTypeAndCollation( a = pSelect->pEList->a; for(i=0, pCol=pTab->aCol; inCol; i++, pCol++){ p = a[i].pExpr; - pCol->zType = sqlite3DbStrDup(db, columnType(&sNC, p,0,0,0, &pCol->szEst)); + if( pCol->zType==0 ){ + pCol->zType = sqlite3DbStrDup(db, columnType(&sNC, p,0,0,0, &pCol->szEst)); + } szAll += pCol->szEst; pCol->affinity = sqlite3ExprAffinity(p); if( pCol->affinity==0 ) pCol->affinity = SQLITE_AFF_NONE; pColl = sqlite3ExprCollSeq(pParse, p); - if( pColl ){ + if( pColl && pCol->zColl==0 ){ pCol->zColl = sqlite3DbStrDup(db, pColl->zName); } } @@ -107117,7 +108287,7 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){ sqlite3ExprCode(pParse, p->pLimit, iLimit); sqlite3VdbeAddOp1(v, OP_MustBeInt, iLimit); VdbeCoverage(v); VdbeComment((v, "LIMIT counter")); - sqlite3VdbeAddOp2(v, OP_IfZero, iLimit, iBreak); VdbeCoverage(v); + sqlite3VdbeAddOp2(v, OP_IfNot, iLimit, iBreak); VdbeCoverage(v); } if( p->pOffset ){ p->iOffset = iOffset = ++pParse->nMem; @@ -107336,7 +108506,7 @@ static void generateWithRecursiveQuery( selectInnerLoop(pParse, p, p->pEList, iCurrent, 0, 0, pDest, addrCont, addrBreak); if( regLimit ){ - sqlite3VdbeAddOp3(v, OP_IfZero, regLimit, addrBreak, -1); + sqlite3VdbeAddOp2(v, OP_DecrJumpZero, regLimit, addrBreak); VdbeCoverage(v); } sqlite3VdbeResolveLabel(v, addrCont); @@ -107402,8 +108572,7 @@ static int multiSelectValues( int nExpr = p->pEList->nExpr; int nRow = 1; int rc = 0; - assert( p->pNext==0 ); - assert( p->selFlags & SF_AllValues ); + assert( p->selFlags & SF_MultiValue ); do{ assert( p->selFlags & SF_Values ); assert( p->op==TK_ALL || (p->op==TK_SELECT && p->pPrior==0) ); @@ -107512,7 +108681,7 @@ static int multiSelect( /* Special handling for a compound-select that originates as a VALUES clause. */ - if( p->selFlags & SF_AllValues ){ + if( p->selFlags & SF_MultiValue ){ rc = multiSelectValues(pParse, p, &dest); goto multi_select_end; } @@ -107561,7 +108730,7 @@ static int multiSelect( p->iLimit = pPrior->iLimit; p->iOffset = pPrior->iOffset; if( p->iLimit ){ - addr = sqlite3VdbeAddOp1(v, OP_IfZero, p->iLimit); VdbeCoverage(v); + addr = sqlite3VdbeAddOp1(v, OP_IfNot, p->iLimit); VdbeCoverage(v); VdbeComment((v, "Jump ahead if LIMIT reached")); } explainSetInteger(iSub2, pParse->iNextSelectId); @@ -107897,7 +109066,7 @@ static int generateOutputSubroutine( */ case SRT_Set: { int r1; - assert( pIn->nSdst==1 ); + assert( pIn->nSdst==1 || pParse->nErr>0 ); pDest->affSdst = sqlite3CompareAffinity(p->pEList->a[0].pExpr, pDest->affSdst); r1 = sqlite3GetTempReg(pParse); @@ -107923,7 +109092,7 @@ static int generateOutputSubroutine( ** of the scan loop. */ case SRT_Mem: { - assert( pIn->nSdst==1 ); + assert( pIn->nSdst==1 || pParse->nErr>0 ); testcase( pIn->nSdst!=1 ); sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSDParm, 1); /* The LIMIT clause will jump out of the loop for us */ break; @@ -107938,7 +109107,7 @@ static int generateOutputSubroutine( pDest->iSdst = sqlite3GetTempRange(pParse, pIn->nSdst); pDest->nSdst = pIn->nSdst; } - sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSdst, pDest->nSdst); + sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSdst, pIn->nSdst); sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm); break; } @@ -107962,7 +109131,7 @@ static int generateOutputSubroutine( /* Jump to the end of the loop if the LIMIT is reached. */ if( p->iLimit ){ - sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1); VdbeCoverage(v); + sqlite3VdbeAddOp2(v, OP_DecrJumpZero, p->iLimit, iBreak); VdbeCoverage(v); } /* Generate the subroutine return @@ -108154,8 +109323,10 @@ static int multiSelectOrderBy( if( aPermute ){ struct ExprList_item *pItem; for(i=0, pItem=pOrderBy->a; iu.x.iOrderByCol>0 - && pItem->u.x.iOrderByCol<=p->pEList->nExpr ); + assert( pItem->u.x.iOrderByCol>0 ); + /* assert( pItem->u.x.iOrderByCol<=p->pEList->nExpr ) is also true + ** but only for well-formed SELECT statements. */ + testcase( pItem->u.x.iOrderByCol > p->pEList->nExpr ); aPermute[i] = pItem->u.x.iOrderByCol - 1; } pKeyMerge = multiSelectOrderByKeyInfo(pParse, p, 1); @@ -108365,7 +109536,7 @@ static int multiSelectOrderBy( /*** TBD: Insert subroutine calls to close cursors on incomplete **** subqueries ****/ explainComposite(pParse, p->op, iSub1, iSub2, 0); - return SQLITE_OK; + return pParse->nErr!=0; } #endif @@ -108485,7 +109656,10 @@ static void substSelect( ** ** (1) The subquery and the outer query do not both use aggregates. ** -** (2) The subquery is not an aggregate or the outer query is not a join. +** (2) The subquery is not an aggregate or (2a) the outer query is not a join +** and (2b) the outer query does not use subqueries other than the one +** FROM-clause subquery that is a candidate for flattening. (2b is +** due to ticket [2f7170d73bf9abf80] from 2015-02-09.) ** ** (3) The subquery is not the right operand of a left outer join ** (Originally ticket #306. Strengthened by ticket #3300) @@ -108622,8 +109796,17 @@ static int flattenSubquery( iParent = pSubitem->iCursor; pSub = pSubitem->pSelect; assert( pSub!=0 ); - if( isAgg && subqueryIsAgg ) return 0; /* Restriction (1) */ - if( subqueryIsAgg && pSrc->nSrc>1 ) return 0; /* Restriction (2) */ + if( subqueryIsAgg ){ + if( isAgg ) return 0; /* Restriction (1) */ + if( pSrc->nSrc>1 ) return 0; /* Restriction (2a) */ + if( (p->pWhere && ExprHasProperty(p->pWhere,EP_Subquery)) + || (sqlite3ExprListFlags(p->pEList) & EP_Subquery)!=0 + || (sqlite3ExprListFlags(p->pOrderBy) & EP_Subquery)!=0 + ){ + return 0; /* Restriction (2b) */ + } + } + pSubSrc = pSub->pSrc; assert( pSubSrc ); /* Prior to version 3.1.2, when LIMIT and OFFSET had to be simple constants, @@ -109165,7 +110348,10 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){ pNew->pOrderBy = 0; p->pPrior = 0; p->pNext = 0; + p->pWith = 0; p->selFlags &= ~SF_Compound; + assert( (p->selFlags & SF_Converted)==0 ); + p->selFlags |= SF_Converted; assert( pNew->pPrior!=0 ); pNew->pPrior->pNext = pNew; pNew->pLimit = 0; @@ -109317,7 +110503,7 @@ static int withExpand( for(pLeft=pSel; pLeft->pPrior; pLeft=pLeft->pPrior); pEList = pLeft->pEList; if( pCte->pCols ){ - if( pEList->nExpr!=pCte->pCols->nExpr ){ + if( pEList && pEList->nExpr!=pCte->pCols->nExpr ){ sqlite3ErrorMsg(pParse, "table %s has %d values for %d columns", pCte->zName, pEList->nExpr, pCte->pCols->nExpr ); @@ -109444,7 +110630,7 @@ static int selectExpander(Walker *pWalker, Select *p){ /* A sub-query in the FROM clause of a SELECT */ assert( pSel!=0 ); assert( pFrom->pTab==0 ); - sqlite3WalkSelect(pWalker, pSel); + if( sqlite3WalkSelect(pWalker, pSel) ) return WRC_Abort; pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table)); if( pTab==0 ) return WRC_Abort; pTab->nRef = 1; @@ -109701,7 +110887,7 @@ static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){ sqlite3WalkSelect(&w, pSelect); } w.xSelectCallback = selectExpander; - if( (pSelect->selFlags & SF_AllValues)==0 ){ + if( (pSelect->selFlags & SF_MultiValue)==0 ){ w.xSelectCallback2 = selectPopWith; } sqlite3WalkSelect(&w, pSelect); @@ -109887,7 +111073,8 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){ } if( pF->iDistinct>=0 ){ addrNext = sqlite3VdbeMakeLabel(v); - assert( nArg==1 ); + testcase( nArg==0 ); /* Error condition */ + testcase( nArg>1 ); /* Also an error */ codeDistinct(pParse, pF->iDistinct, addrNext, 1, regAgg); } if( pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ @@ -110043,6 +111230,13 @@ SQLITE_PRIVATE int sqlite3Select( } isAgg = (p->selFlags & SF_Aggregate)!=0; assert( pEList!=0 ); +#if SELECTTRACE_ENABLED + if( sqlite3SelectTrace & 0x100 ){ + SELECTTRACE(0x100,pParse,p, ("after name resolution:\n")); + sqlite3TreeViewSelect(0, p, 0); + } +#endif + /* Begin generating code. */ @@ -110755,10 +111949,9 @@ SQLITE_PRIVATE int sqlite3Select( */ sqlite3VdbeResolveLabel(v, iEnd); - /* The SELECT was successfully coded. Set the return code to 0 - ** to indicate no errors. - */ - rc = 0; + /* The SELECT has been coded. If there is an error in the Parse structure, + ** set the return code to 1. Otherwise 0. */ + rc = (pParse->nErr>0); /* Control jumps to here if an error is encountered above, or upon ** successful coding of the SELECT. @@ -110788,9 +111981,9 @@ select_end: SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){ int n = 0; pView = sqlite3TreeViewPush(pView, moreToFollow); - sqlite3TreeViewLine(pView, "SELECT%s%s", + sqlite3TreeViewLine(pView, "SELECT%s%s (0x%p)", ((p->selFlags & SF_Distinct) ? " DISTINCT" : ""), - ((p->selFlags & SF_Aggregate) ? " agg_flag" : "") + ((p->selFlags & SF_Aggregate) ? " agg_flag" : ""), p ); if( p->pSrc && p->pSrc->nSrc ) n++; if( p->pWhere ) n++; @@ -110809,7 +112002,7 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m struct SrcList_item *pItem = &p->pSrc->a[i]; StrAccum x; char zLine[100]; - sqlite3StrAccumInit(&x, zLine, sizeof(zLine), 0); + sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0); sqlite3XPrintf(&x, 0, "{%d,*}", pItem->iCursor); if( pItem->zDatabase ){ sqlite3XPrintf(&x, 0, " %s.%s", pItem->zDatabase, pItem->zName); @@ -110968,7 +112161,7 @@ static int sqlite3_get_table_cb(void *pArg, int nCol, char **argv, char **colv){ z = 0; }else{ int n = sqlite3Strlen30(argv[i])+1; - z = sqlite3_malloc( n ); + z = sqlite3_malloc64( n ); if( z==0 ) goto malloc_failed; memcpy(z, argv[i], n); } @@ -110993,7 +112186,7 @@ malloc_failed: ** Instead, the entire table should be passed to sqlite3_free_table() when ** the calling procedure is finished using it. */ -SQLITE_API int sqlite3_get_table( +SQLITE_API int SQLITE_STDCALL sqlite3_get_table( sqlite3 *db, /* The database on which the SQL executes */ const char *zSql, /* The SQL to be executed */ char ***pazResult, /* Write the result table here */ @@ -111017,7 +112210,7 @@ SQLITE_API int sqlite3_get_table( res.nData = 1; res.nAlloc = 20; res.rc = SQLITE_OK; - res.azResult = sqlite3_malloc(sizeof(char*)*res.nAlloc ); + res.azResult = sqlite3_malloc64(sizeof(char*)*res.nAlloc ); if( res.azResult==0 ){ db->errCode = SQLITE_NOMEM; return SQLITE_NOMEM; @@ -111045,7 +112238,7 @@ SQLITE_API int sqlite3_get_table( } if( res.nAlloc>res.nData ){ char **azNew; - azNew = sqlite3_realloc( res.azResult, sizeof(char*)*res.nData ); + azNew = sqlite3_realloc64( res.azResult, sizeof(char*)*res.nData ); if( azNew==0 ){ sqlite3_free_table(&res.azResult[1]); db->errCode = SQLITE_NOMEM; @@ -111062,7 +112255,7 @@ SQLITE_API int sqlite3_get_table( /* ** This routine frees the space the sqlite3_get_table() malloced. */ -SQLITE_API void sqlite3_free_table( +SQLITE_API void SQLITE_STDCALL sqlite3_free_table( char **azResult /* Result returned from sqlite3_get_table() */ ){ if( azResult ){ @@ -111273,7 +112466,6 @@ SQLITE_PRIVATE void sqlite3BeginTrigger( /* Do not create a trigger on a system table */ if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 ){ sqlite3ErrorMsg(pParse, "cannot create trigger on system table"); - pParse->nErr++; goto trigger_cleanup; } @@ -111453,12 +112645,12 @@ static TriggerStep *triggerStepAllocate( ){ TriggerStep *pTriggerStep; - pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep) + pName->n); + pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep) + pName->n + 1); if( pTriggerStep ){ char *z = (char*)&pTriggerStep[1]; memcpy(z, pName->z, pName->n); - pTriggerStep->target.z = z; - pTriggerStep->target.n = pName->n; + sqlite3Dequote(z); + pTriggerStep->zTarget = z; pTriggerStep->op = op; } return pTriggerStep; @@ -111741,7 +112933,7 @@ SQLITE_PRIVATE Trigger *sqlite3TriggersExist( } /* -** Convert the pStep->target token into a SrcList and return a pointer +** Convert the pStep->zTarget string into a SrcList and return a pointer ** to that SrcList. ** ** This routine adds a specific database name, if needed, to the target when @@ -111754,17 +112946,17 @@ static SrcList *targetSrcList( Parse *pParse, /* The parsing context */ TriggerStep *pStep /* The trigger containing the target token */ ){ + sqlite3 *db = pParse->db; int iDb; /* Index of the database to use */ SrcList *pSrc; /* SrcList to be returned */ - pSrc = sqlite3SrcListAppend(pParse->db, 0, &pStep->target, 0); + pSrc = sqlite3SrcListAppend(db, 0, 0, 0); if( pSrc ){ assert( pSrc->nSrc>0 ); - assert( pSrc->a!=0 ); - iDb = sqlite3SchemaToIndex(pParse->db, pStep->pTrig->pSchema); + pSrc->a[pSrc->nSrc-1].zName = sqlite3DbStrDup(db, pStep->zTarget); + iDb = sqlite3SchemaToIndex(db, pStep->pTrig->pSchema); if( iDb==0 || iDb>=2 ){ - sqlite3 *db = pParse->db; - assert( iDbdb->nDb ); + assert( iDbnDb ); pSrc->a[pSrc->nSrc-1].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zName); } } @@ -111876,6 +113068,7 @@ static void transferParseError(Parse *pTo, Parse *pFrom){ if( pTo->nErr==0 ){ pTo->zErrMsg = pFrom->zErrMsg; pTo->nErr = pFrom->nErr; + pTo->rc = pFrom->rc; }else{ sqlite3DbFree(pFrom->db, pFrom->zErrMsg); } @@ -113160,7 +114353,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ ** cause problems for the call to BtreeSetPageSize() below. */ sqlite3BtreeCommit(pTemp); - nRes = sqlite3BtreeGetReserve(pMain); + nRes = sqlite3BtreeGetOptimalReserve(pMain); /* A VACUUM cannot change the pagesize of an encrypted database. */ #ifdef SQLITE_HAS_CODEC @@ -113226,6 +114419,8 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ ** an "INSERT INTO vacuum_db.xxx SELECT * FROM main.xxx;" to copy ** the contents to the temporary database. */ + assert( (db->flags & SQLITE_Vacuum)==0 ); + db->flags |= SQLITE_Vacuum; rc = execExecSql(db, pzErrMsg, "SELECT 'INSERT INTO vacuum_db.' || quote(name) " "|| ' SELECT * FROM main.' || quote(name) || ';'" @@ -113233,6 +114428,8 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ "WHERE type = 'table' AND name!='sqlite_sequence' " " AND coalesce(rootpage,1)>0" ); + assert( (db->flags & SQLITE_Vacuum)!=0 ); + db->flags &= ~SQLITE_Vacuum; if( rc!=SQLITE_OK ) goto end_of_vacuum; /* Copy over the sequence table @@ -113371,6 +114568,8 @@ end_of_vacuum: struct VtabCtx { VTable *pVTable; /* The virtual table being constructed */ Table *pTab; /* The Table object to which the virtual table belongs */ + VtabCtx *pPrior; /* Parent context (if any) */ + int bDeclared; /* True after sqlite3_declare_vtab() is called */ }; /* @@ -113422,7 +114621,7 @@ static int createModule( /* ** External API function used to create a new virtual-table module. */ -SQLITE_API int sqlite3_create_module( +SQLITE_API int SQLITE_STDCALL sqlite3_create_module( sqlite3 *db, /* Database in which module is registered */ const char *zName, /* Name assigned to this module */ const sqlite3_module *pModule, /* The definition of the module */ @@ -113437,7 +114636,7 @@ SQLITE_API int sqlite3_create_module( /* ** External API function used to create a new virtual-table module. */ -SQLITE_API int sqlite3_create_module_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_create_module_v2( sqlite3 *db, /* Database in which module is registered */ const char *zName, /* Name assigned to this module */ const sqlite3_module *pModule, /* The definition of the module */ @@ -113736,6 +114935,7 @@ SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ char *zStmt; char *zWhere; int iDb; + int iReg; Vdbe *v; /* Compute the complete text of the CREATE VIRTUAL TABLE statement */ @@ -113770,8 +114970,10 @@ SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ sqlite3VdbeAddOp2(v, OP_Expire, 0, 0); zWhere = sqlite3MPrintf(db, "name='%q' AND type='table'", pTab->zName); sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere); - sqlite3VdbeAddOp4(v, OP_VCreate, iDb, 0, 0, - pTab->zName, sqlite3Strlen30(pTab->zName) + 1); + + iReg = ++pParse->nMem; + sqlite3VdbeAddOp4(v, OP_String8, 0, iReg, 0, pTab->zName, 0); + sqlite3VdbeAddOp2(v, OP_VCreate, iDb, iReg); } /* If we are rereading the sqlite_master table create the in-memory @@ -113814,7 +115016,7 @@ SQLITE_PRIVATE void sqlite3VtabArgExtend(Parse *pParse, Token *p){ pArg->z = p->z; pArg->n = p->n; }else{ - assert(pArg->z < p->z); + assert(pArg->z <= p->z); pArg->n = (int)(&p->z[p->n] - pArg->z); } } @@ -113831,15 +115033,27 @@ static int vtabCallConstructor( int (*xConstruct)(sqlite3*,void*,int,const char*const*,sqlite3_vtab**,char**), char **pzErr ){ - VtabCtx sCtx, *pPriorCtx; + VtabCtx sCtx; VTable *pVTable; int rc; const char *const*azArg = (const char *const*)pTab->azModuleArg; int nArg = pTab->nModuleArg; char *zErr = 0; - char *zModuleName = sqlite3MPrintf(db, "%s", pTab->zName); + char *zModuleName; int iDb; + VtabCtx *pCtx; + /* Check that the virtual-table is not already being initialized */ + for(pCtx=db->pVtabCtx; pCtx; pCtx=pCtx->pPrior){ + if( pCtx->pTab==pTab ){ + *pzErr = sqlite3MPrintf(db, + "vtable constructor called recursively: %s", pTab->zName + ); + return SQLITE_LOCKED; + } + } + + zModuleName = sqlite3MPrintf(db, "%s", pTab->zName); if( !zModuleName ){ return SQLITE_NOMEM; } @@ -113860,11 +115074,13 @@ static int vtabCallConstructor( assert( xConstruct ); sCtx.pTab = pTab; sCtx.pVTable = pVTable; - pPriorCtx = db->pVtabCtx; + sCtx.pPrior = db->pVtabCtx; + sCtx.bDeclared = 0; db->pVtabCtx = &sCtx; rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr); - db->pVtabCtx = pPriorCtx; + db->pVtabCtx = sCtx.pPrior; if( rc==SQLITE_NOMEM ) db->mallocFailed = 1; + assert( sCtx.pTab==pTab ); if( SQLITE_OK!=rc ){ if( zErr==0 ){ @@ -113880,13 +115096,14 @@ static int vtabCallConstructor( memset(pVTable->pVtab, 0, sizeof(pVTable->pVtab[0])); pVTable->pVtab->pModule = pMod->pModule; pVTable->nRef = 1; - if( sCtx.pTab ){ + if( sCtx.bDeclared==0 ){ const char *zFormat = "vtable constructor did not declare schema: %s"; *pzErr = sqlite3MPrintf(db, zFormat, pTab->zName); sqlite3VtabUnlock(pVTable); rc = SQLITE_ERROR; }else{ int iCol; + u8 oooHidden = 0; /* If everything went according to plan, link the new VTable structure ** into the linked list headed by pTab->pVTable. Then loop through the ** columns of the table to see if any of them contain the token "hidden". @@ -113899,7 +115116,10 @@ static int vtabCallConstructor( char *zType = pTab->aCol[iCol].zType; int nType; int i = 0; - if( !zType ) continue; + if( !zType ){ + pTab->tabFlags |= oooHidden; + continue; + } nType = sqlite3Strlen30(zType); if( sqlite3StrNICmp("hidden", zType, 6)||(zType[6] && zType[6]!=' ') ){ for(i=0; iaCol[iCol].colFlags |= COLFLAG_HIDDEN; + oooHidden = TF_OOOHidden; + }else{ + pTab->tabFlags |= oooHidden; } } } @@ -114049,22 +115272,26 @@ SQLITE_PRIVATE int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, ** valid to call this function from within the xCreate() or xConnect() of a ** virtual table module. */ -SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ +SQLITE_API int SQLITE_STDCALL sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ + VtabCtx *pCtx; Parse *pParse; - int rc = SQLITE_OK; Table *pTab; char *zErr = 0; #ifdef SQLITE_ENABLE_API_ARMOR - if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; + if( !sqlite3SafetyCheckOk(db) || zCreateTable==0 ){ + return SQLITE_MISUSE_BKPT; + } #endif sqlite3_mutex_enter(db->mutex); - if( !db->pVtabCtx || !(pTab = db->pVtabCtx->pTab) ){ + pCtx = db->pVtabCtx; + if( !pCtx || pCtx->bDeclared ){ sqlite3Error(db, SQLITE_MISUSE); sqlite3_mutex_leave(db->mutex); return SQLITE_MISUSE_BKPT; } + pTab = pCtx->pTab; assert( (pTab->tabFlags & TF_Virtual)!=0 ); pParse = sqlite3StackAllocZero(db, sizeof(*pParse)); @@ -114087,7 +115314,7 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ pParse->pNewTable->nCol = 0; pParse->pNewTable->aCol = 0; } - db->pVtabCtx->pTab = 0; + pCtx->bDeclared = 1; }else{ sqlite3ErrorWithMsg(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr); sqlite3DbFree(db, zErr); @@ -114122,11 +115349,15 @@ SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName); if( ALWAYS(pTab!=0 && pTab->pVTable!=0) ){ - VTable *p = vtabDisconnectAll(db, pTab); - - assert( rc==SQLITE_OK ); + VTable *p; + for(p=pTab->pVTable; p; p=p->pNext){ + assert( p->pVtab ); + if( p->pVtab->nRef>0 ){ + return SQLITE_LOCKED; + } + } + p = vtabDisconnectAll(db, pTab); rc = p->pMod->pModule->xDestroy(p->pVtab); - /* Remove the sqlite3_vtab* from the aVTrans[] array, if applicable */ if( rc==SQLITE_OK ){ assert( pTab->pVTable==p && p->pNext==0 ); @@ -114277,7 +115508,7 @@ SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){ int rc = SQLITE_OK; assert( op==SAVEPOINT_RELEASE||op==SAVEPOINT_ROLLBACK||op==SAVEPOINT_BEGIN ); - assert( iSavepoint>=0 ); + assert( iSavepoint>=-1 ); if( db->aVTrans ){ int i; for(i=0; rc==SQLITE_OK && inVTrans; i++){ @@ -114395,7 +115626,7 @@ SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){ if( pTab==pToplevel->apVtabLock[i] ) return; } n = (pToplevel->nVtabLock+1)*sizeof(pToplevel->apVtabLock[0]); - apVtabLock = sqlite3_realloc(pToplevel->apVtabLock, n); + apVtabLock = sqlite3_realloc64(pToplevel->apVtabLock, n); if( apVtabLock ){ pToplevel->apVtabLock = apVtabLock; pToplevel->apVtabLock[pToplevel->nVtabLock++] = pTab; @@ -114411,7 +115642,7 @@ SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){ ** The results of this routine are undefined unless it is called from ** within an xUpdate method. */ -SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *db){ +SQLITE_API int SQLITE_STDCALL sqlite3_vtab_on_conflict(sqlite3 *db){ static const unsigned char aMap[] = { SQLITE_ROLLBACK, SQLITE_ABORT, SQLITE_FAIL, SQLITE_IGNORE, SQLITE_REPLACE }; @@ -114429,7 +115660,7 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *db){ ** the SQLite core with additional information about the behavior ** of the virtual table being implemented. */ -SQLITE_API int sqlite3_vtab_config(sqlite3 *db, int op, ...){ +SQLITE_API int SQLITE_CDECL sqlite3_vtab_config(sqlite3 *db, int op, ...){ va_list ap; int rc = SQLITE_OK; @@ -114555,6 +115786,8 @@ struct WhereLevel { int addrCont; /* Jump here to continue with the next loop cycle */ int addrFirst; /* First instruction of interior of the loop */ int addrBody; /* Beginning of the body of this loop */ + int iLikeRepCntr; /* LIKE range processing counter register */ + int addrLikeRep; /* LIKE range processing address */ u8 iFrom; /* Which entry in the FROM clause */ u8 op, p3, p5; /* Opcode, P3 & P5 of the opcode that ends the loop */ int p1, p2; /* Operands of the opcode used to ends the loop */ @@ -114739,7 +115972,7 @@ struct WhereTerm { } u; LogEst truthProb; /* Probability of truth for this expression */ u16 eOperator; /* A WO_xx value describing */ - u8 wtFlags; /* TERM_xxx bit flags. See below */ + u16 wtFlags; /* TERM_xxx bit flags. See below */ u8 nChild; /* Number of children that must disable us */ WhereClause *pWC; /* The clause this term is part of */ Bitmask prereqRight; /* Bitmask of tables used by pExpr->pRight */ @@ -114761,6 +115994,9 @@ struct WhereTerm { #else # define TERM_VNULL 0x00 /* Disabled if not using stat3 */ #endif +#define TERM_LIKEOPT 0x100 /* Virtual terms from the LIKE optimization */ +#define TERM_LIKECOND 0x200 /* Conditionally this LIKE operator term */ +#define TERM_LIKE 0x400 /* The original LIKE operator */ /* ** An instance of the WhereScan object is used as an iterator for locating @@ -115136,7 +116372,7 @@ static void whereClauseClear(WhereClause *pWC){ ** calling this routine. Such pointers may be reinitialized by referencing ** the pWC->a[] array. */ -static int whereClauseInsert(WhereClause *pWC, Expr *p, u8 wtFlags){ +static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){ WhereTerm *pTerm; int idx; testcase( wtFlags & TERM_VIRTUAL ); @@ -115189,13 +116425,14 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u8 wtFlags){ ** all terms of the WHERE clause. */ static void whereSplit(WhereClause *pWC, Expr *pExpr, u8 op){ + Expr *pE2 = sqlite3ExprSkipCollate(pExpr); pWC->op = op; - if( pExpr==0 ) return; - if( pExpr->op!=op ){ + if( pE2==0 ) return; + if( pE2->op!=op ){ whereClauseInsert(pWC, pExpr, 0); }else{ - whereSplit(pWC, pExpr->pLeft, op); - whereSplit(pWC, pExpr->pRight, op); + whereSplit(pWC, pE2->pLeft, op); + whereSplit(pWC, pE2->pRight, op); } } @@ -115561,7 +116798,11 @@ static void exprAnalyzeAll( ** so and false if not. ** ** In order for the operator to be optimizible, the RHS must be a string -** literal that does not begin with a wildcard. +** literal that does not begin with a wildcard. The LHS must be a column +** that may only be NULL, a string, or a BLOB, never a number. (This means +** that virtual tables cannot participate in the LIKE optimization.) If the +** collating sequence for the column on the LHS must be appropriate for +** the operator. */ static int isLikeOrGlob( Parse *pParse, /* Parsing and code generating context */ @@ -115590,7 +116831,7 @@ static int isLikeOrGlob( pLeft = pList->a[1].pExpr; if( pLeft->op!=TK_COLUMN || sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT - || IsVirtual(pLeft->pTab) + || IsVirtual(pLeft->pTab) /* Value might be numeric */ ){ /* IMP: R-02065-49465 The left-hand side of the LIKE or GLOB operator must ** be the name of an indexed column with TEXT affinity. */ @@ -115700,6 +116941,79 @@ static void markTermAsChild(WhereClause *pWC, int iChild, int iParent){ pWC->a[iParent].nChild++; } +/* +** Return the N-th AND-connected subterm of pTerm. Or if pTerm is not +** a conjunction, then return just pTerm when N==0. If N is exceeds +** the number of available subterms, return NULL. +*/ +static WhereTerm *whereNthSubterm(WhereTerm *pTerm, int N){ + if( pTerm->eOperator!=WO_AND ){ + return N==0 ? pTerm : 0; + } + if( Nu.pAndInfo->wc.nTerm ){ + return &pTerm->u.pAndInfo->wc.a[N]; + } + return 0; +} + +/* +** Subterms pOne and pTwo are contained within WHERE clause pWC. The +** two subterms are in disjunction - they are OR-ed together. +** +** If these two terms are both of the form: "A op B" with the same +** A and B values but different operators and if the operators are +** compatible (if one is = and the other is <, for example) then +** add a new virtual AND term to pWC that is the combination of the +** two. +** +** Some examples: +** +** x x<=y +** x=y OR x=y --> x=y +** x<=y OR x x<=y +** +** The following is NOT generated: +** +** xy --> x!=y +*/ +static void whereCombineDisjuncts( + SrcList *pSrc, /* the FROM clause */ + WhereClause *pWC, /* The complete WHERE clause */ + WhereTerm *pOne, /* First disjunct */ + WhereTerm *pTwo /* Second disjunct */ +){ + u16 eOp = pOne->eOperator | pTwo->eOperator; + sqlite3 *db; /* Database connection (for malloc) */ + Expr *pNew; /* New virtual expression */ + int op; /* Operator for the combined expression */ + int idxNew; /* Index in pWC of the next virtual term */ + + if( (pOne->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return; + if( (pTwo->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return; + if( (eOp & (WO_EQ|WO_LT|WO_LE))!=eOp + && (eOp & (WO_EQ|WO_GT|WO_GE))!=eOp ) return; + assert( pOne->pExpr->pLeft!=0 && pOne->pExpr->pRight!=0 ); + assert( pTwo->pExpr->pLeft!=0 && pTwo->pExpr->pRight!=0 ); + if( sqlite3ExprCompare(pOne->pExpr->pLeft, pTwo->pExpr->pLeft, -1) ) return; + if( sqlite3ExprCompare(pOne->pExpr->pRight, pTwo->pExpr->pRight, -1) )return; + /* If we reach this point, it means the two subterms can be combined */ + if( (eOp & (eOp-1))!=0 ){ + if( eOp & (WO_LT|WO_LE) ){ + eOp = WO_LE; + }else{ + assert( eOp & (WO_GT|WO_GE) ); + eOp = WO_GE; + } + } + db = pWC->pWInfo->pParse->db; + pNew = sqlite3ExprDup(db, pOne->pExpr, 0); + if( pNew==0 ) return; + for(op=TK_EQ; eOp!=(WO_EQ<<(op-TK_EQ)); op++){ assert( opop = op; + idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC); + exprAnalyze(pSrc, pWC, idxNew); +} + #if !defined(SQLITE_OMIT_OR_OPTIMIZATION) && !defined(SQLITE_OMIT_SUBQUERY) /* ** Analyze a term that consists of two or more OR-connected @@ -115724,6 +117038,7 @@ static void markTermAsChild(WhereClause *pWC, int iChild, int iParent){ ** (C) t1.x=t2.y OR (t1.x=t2.z AND t1.y=15) ** (D) x=expr1 OR (y>11 AND y<22 AND z LIKE '*hello*') ** (E) (p.a=1 AND q.b=2 AND r.c=3) OR (p.x=4 AND q.y=5 AND r.z=6) +** (F) x>A OR (x=A AND y>=B) ** ** CASE 1: ** @@ -115740,6 +117055,16 @@ static void markTermAsChild(WhereClause *pWC, int iChild, int iParent){ ** ** CASE 2: ** +** If there are exactly two disjuncts one side has x>A and the other side +** has x=A (for the same x and A) then add a new virtual conjunct term to the +** WHERE clause of the form "x>=A". Example: +** +** x>A OR (x=A AND y>B) adds: x>=A +** +** The added conjunct can sometimes be helpful in query planning. +** +** CASE 3: +** ** If all subterms are indexable by a single table T, then set ** ** WhereTerm.eOperator = WO_OR @@ -115866,12 +117191,26 @@ static void exprAnalyzeOrTerm( } /* - ** Record the set of tables that satisfy case 2. The set might be + ** Record the set of tables that satisfy case 3. The set might be ** empty. */ pOrInfo->indexable = indexable; pTerm->eOperator = indexable==0 ? 0 : WO_OR; + /* For a two-way OR, attempt to implementation case 2. + */ + if( indexable && pOrWc->nTerm==2 ){ + int iOne = 0; + WhereTerm *pOne; + while( (pOne = whereNthSubterm(&pOrWc->a[0],iOne++))!=0 ){ + int iTwo = 0; + WhereTerm *pTwo; + while( (pTwo = whereNthSubterm(&pOrWc->a[1],iTwo++))!=0 ){ + whereCombineDisjuncts(pSrc, pWC, pOne, pTwo); + } + } + } + /* ** chngToIN holds a set of tables that *might* satisfy case 1. But ** we have to do some additional checking to see if case 1 really @@ -116001,7 +117340,7 @@ static void exprAnalyzeOrTerm( }else{ sqlite3ExprListDelete(db, pList); } - pTerm->eOperator = WO_NOOP; /* case 1 trumps case 2 */ + pTerm->eOperator = WO_NOOP; /* case 1 trumps case 3 */ } } } @@ -116039,7 +117378,7 @@ static void exprAnalyze( Bitmask extraRight = 0; /* Extra dependencies on LEFT JOIN */ Expr *pStr1 = 0; /* RHS of LIKE/GLOB operator */ int isComplete = 0; /* RHS of LIKE/GLOB ends with wildcard */ - int noCase = 0; /* LIKE/GLOB distinguishes case */ + int noCase = 0; /* uppercase equivalent to lowercase */ int op; /* Top-level operator. pExpr->op */ Parse *pParse = pWInfo->pParse; /* Parsing context */ sqlite3 *db = pParse->db; /* Database connection */ @@ -116177,12 +117516,15 @@ static void exprAnalyze( /* Add constraints to reduce the search space on a LIKE or GLOB ** operator. ** - ** A like pattern of the form "x LIKE 'abc%'" is changed into constraints + ** A like pattern of the form "x LIKE 'aBc%'" is changed into constraints ** - ** x>='abc' AND x<'abd' AND x LIKE 'abc%' + ** x>='ABC' AND x<'abd' AND x LIKE 'aBc%' ** ** The last character of the prefix "abc" is incremented to form the - ** termination condition "abd". + ** termination condition "abd". If case is not significant (the default + ** for LIKE) then the lower-bound is made all uppercase and the upper- + ** bound is made all lowercase so that the bounds also work when comparing + ** BLOBs. */ if( pWC->op==TK_AND && isLikeOrGlob(pParse, pExpr, &pStr1, &isComplete, &noCase) @@ -116193,10 +117535,26 @@ static void exprAnalyze( Expr *pNewExpr2; int idxNew1; int idxNew2; - Token sCollSeqName; /* Name of collating sequence */ + const char *zCollSeqName; /* Name of collating sequence */ + const u16 wtFlags = TERM_LIKEOPT | TERM_VIRTUAL | TERM_DYNAMIC; pLeft = pExpr->x.pList->a[1].pExpr; pStr2 = sqlite3ExprDup(db, pStr1, 0); + + /* Convert the lower bound to upper-case and the upper bound to + ** lower-case (upper-case is less than lower-case in ASCII) so that + ** the range constraints also work for BLOBs + */ + if( noCase && !pParse->db->mallocFailed ){ + int i; + char c; + pTerm->wtFlags |= TERM_LIKE; + for(i=0; (c = pStr1->u.zToken[i])!=0; i++){ + pStr1->u.zToken[i] = sqlite3Toupper(c); + pStr2->u.zToken[i] = sqlite3Tolower(c); + } + } + if( !db->mallocFailed ){ u8 c, *pC; /* Last character before the first wildcard */ pC = (u8*)&pStr2->u.zToken[sqlite3Strlen30(pStr2->u.zToken)-1]; @@ -116213,22 +117571,21 @@ static void exprAnalyze( } *pC = c + 1; } - sCollSeqName.z = noCase ? "NOCASE" : "BINARY"; - sCollSeqName.n = 6; + zCollSeqName = noCase ? "NOCASE" : "BINARY"; pNewExpr1 = sqlite3ExprDup(db, pLeft, 0); - pNewExpr1 = sqlite3PExpr(pParse, TK_GE, - sqlite3ExprAddCollateToken(pParse,pNewExpr1,&sCollSeqName), + pNewExpr1 = sqlite3PExpr(pParse, TK_GE, + sqlite3ExprAddCollateString(pParse,pNewExpr1,zCollSeqName), pStr1, 0); transferJoinMarkings(pNewExpr1, pExpr); - idxNew1 = whereClauseInsert(pWC, pNewExpr1, TERM_VIRTUAL|TERM_DYNAMIC); + idxNew1 = whereClauseInsert(pWC, pNewExpr1, wtFlags); testcase( idxNew1==0 ); exprAnalyze(pSrc, pWC, idxNew1); pNewExpr2 = sqlite3ExprDup(db, pLeft, 0); pNewExpr2 = sqlite3PExpr(pParse, TK_LT, - sqlite3ExprAddCollateToken(pParse,pNewExpr2,&sCollSeqName), + sqlite3ExprAddCollateString(pParse,pNewExpr2,zCollSeqName), pStr2, 0); transferJoinMarkings(pNewExpr2, pExpr); - idxNew2 = whereClauseInsert(pWC, pNewExpr2, TERM_VIRTUAL|TERM_DYNAMIC); + idxNew2 = whereClauseInsert(pWC, pNewExpr2, wtFlags); testcase( idxNew2==0 ); exprAnalyze(pSrc, pWC, idxNew2); pTerm = &pWC->a[idxTerm]; @@ -116346,7 +117703,7 @@ static int findIndexCol( && p->iTable==iBase ){ CollSeq *pColl = sqlite3ExprCollSeq(pParse, pList->a[i].pExpr); - if( ALWAYS(pColl) && 0==sqlite3StrICmp(pColl->zName, zColl) ){ + if( pColl && 0==sqlite3StrICmp(pColl->zName, zColl) ){ return i; } } @@ -116546,11 +117903,16 @@ static void constructAutomaticIndex( pLoop = pLevel->pWLoop; idxCols = 0; for(pTerm=pWC->a; pTermpExpr; + assert( !ExprHasProperty(pExpr, EP_FromJoin) /* prereq always non-zero */ + || pExpr->iRightJoinTable!=pSrc->iCursor /* for the right-hand */ + || pLoop->prereq!=0 ); /* table of a LEFT JOIN */ if( pLoop->prereq==0 && (pTerm->wtFlags & TERM_VIRTUAL)==0 - && sqlite3ExprIsTableConstant(pTerm->pExpr, pSrc->iCursor) ){ + && !ExprHasProperty(pExpr, EP_FromJoin) + && sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor) ){ pPartial = sqlite3ExprAnd(pParse->db, pPartial, - sqlite3ExprDup(pParse->db, pTerm->pExpr, 0)); + sqlite3ExprDup(pParse->db, pExpr, 0)); } if( termCanDriveIndex(pTerm, pSrc, notReady) ){ int iCol = pTerm->u.leftColumn; @@ -116615,7 +117977,7 @@ static void constructAutomaticIndex( idxCols |= cMask; pIdx->aiColumn[n] = pTerm->u.leftColumn; pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); - pIdx->azColl[n] = ALWAYS(pColl) ? pColl->zName : "BINARY"; + pIdx->azColl[n] = pColl ? pColl->zName : "BINARY"; n++; } } @@ -116837,11 +118199,14 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ ** Estimate the location of a particular key among all keys in an ** index. Store the results in aStat as follows: ** -** aStat[0] Est. number of rows less than pVal -** aStat[1] Est. number of rows equal to pVal +** aStat[0] Est. number of rows less than pRec +** aStat[1] Est. number of rows equal to pRec ** ** Return the index of the sample that is the smallest sample that -** is greater than or equal to pRec. +** is greater than or equal to pRec. Note that this index is not an index +** into the aSample[] array - it is an index into a virtual set of samples +** based on the contents of aSample[] and the number of fields in record +** pRec. */ static int whereKeyStats( Parse *pParse, /* Database connection */ @@ -116852,67 +118217,158 @@ static int whereKeyStats( ){ IndexSample *aSample = pIdx->aSample; int iCol; /* Index of required stats in anEq[] etc. */ + int i; /* Index of first sample >= pRec */ + int iSample; /* Smallest sample larger than or equal to pRec */ int iMin = 0; /* Smallest sample not yet tested */ - int i = pIdx->nSample; /* Smallest sample larger than or equal to pRec */ int iTest; /* Next sample to test */ int res; /* Result of comparison operation */ + int nField; /* Number of fields in pRec */ + tRowcnt iLower = 0; /* anLt[] + anEq[] of largest sample pRec is > */ #ifndef SQLITE_DEBUG UNUSED_PARAMETER( pParse ); #endif assert( pRec!=0 ); - iCol = pRec->nField - 1; assert( pIdx->nSample>0 ); - assert( pRec->nField>0 && iColnSampleCol ); + assert( pRec->nField>0 && pRec->nField<=pIdx->nSampleCol ); + + /* Do a binary search to find the first sample greater than or equal + ** to pRec. If pRec contains a single field, the set of samples to search + ** is simply the aSample[] array. If the samples in aSample[] contain more + ** than one fields, all fields following the first are ignored. + ** + ** If pRec contains N fields, where N is more than one, then as well as the + ** samples in aSample[] (truncated to N fields), the search also has to + ** consider prefixes of those samples. For example, if the set of samples + ** in aSample is: + ** + ** aSample[0] = (a, 5) + ** aSample[1] = (a, 10) + ** aSample[2] = (b, 5) + ** aSample[3] = (c, 100) + ** aSample[4] = (c, 105) + ** + ** Then the search space should ideally be the samples above and the + ** unique prefixes [a], [b] and [c]. But since that is hard to organize, + ** the code actually searches this set: + ** + ** 0: (a) + ** 1: (a, 5) + ** 2: (a, 10) + ** 3: (a, 10) + ** 4: (b) + ** 5: (b, 5) + ** 6: (c) + ** 7: (c, 100) + ** 8: (c, 105) + ** 9: (c, 105) + ** + ** For each sample in the aSample[] array, N samples are present in the + ** effective sample array. In the above, samples 0 and 1 are based on + ** sample aSample[0]. Samples 2 and 3 on aSample[1] etc. + ** + ** Often, sample i of each block of N effective samples has (i+1) fields. + ** Except, each sample may be extended to ensure that it is greater than or + ** equal to the previous sample in the array. For example, in the above, + ** sample 2 is the first sample of a block of N samples, so at first it + ** appears that it should be 1 field in size. However, that would make it + ** smaller than sample 1, so the binary search would not work. As a result, + ** it is extended to two fields. The duplicates that this creates do not + ** cause any problems. + */ + nField = pRec->nField; + iCol = 0; + iSample = pIdx->nSample * nField; do{ - iTest = (iMin+i)/2; - res = sqlite3VdbeRecordCompare(aSample[iTest].n, aSample[iTest].p, pRec); - if( res<0 ){ - iMin = iTest+1; + int iSamp; /* Index in aSample[] of test sample */ + int n; /* Number of fields in test sample */ + + iTest = (iMin+iSample)/2; + iSamp = iTest / nField; + if( iSamp>0 ){ + /* The proposed effective sample is a prefix of sample aSample[iSamp]. + ** Specifically, the shortest prefix of at least (1 + iTest%nField) + ** fields that is greater than the previous effective sample. */ + for(n=(iTest % nField) + 1; nnField = n; + res = sqlite3VdbeRecordCompare(aSample[iSamp].n, aSample[iSamp].p, pRec); + if( res<0 ){ + iLower = aSample[iSamp].anLt[n-1] + aSample[iSamp].anEq[n-1]; + iMin = iTest+1; + }else if( res==0 && nnSample ); - assert( 0==sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec) - || pParse->db->mallocFailed ); - }else{ - /* Otherwise, pRec must be smaller than sample $i and larger than - ** sample ($i-1). */ - assert( i==pIdx->nSample - || sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)>0 - || pParse->db->mallocFailed ); - assert( i==0 - || sqlite3VdbeRecordCompare(aSample[i-1].n, aSample[i-1].p, pRec)<0 - || pParse->db->mallocFailed ); + if( pParse->db->mallocFailed==0 ){ + if( res==0 ){ + /* If (res==0) is true, then pRec must be equal to sample i. */ + assert( inSample ); + assert( iCol==nField-1 ); + pRec->nField = nField; + assert( 0==sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec) + || pParse->db->mallocFailed + ); + }else{ + /* Unless i==pIdx->nSample, indicating that pRec is larger than + ** all samples in the aSample[] array, pRec must be smaller than the + ** (iCol+1) field prefix of sample i. */ + assert( i<=pIdx->nSample && i>=0 ); + pRec->nField = iCol+1; + assert( i==pIdx->nSample + || sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)>0 + || pParse->db->mallocFailed ); + + /* if i==0 and iCol==0, then record pRec is smaller than all samples + ** in the aSample[] array. Otherwise, if (iCol>0) then pRec must + ** be greater than or equal to the (iCol) field prefix of sample i. + ** If (i>0), then pRec must also be greater than sample (i-1). */ + if( iCol>0 ){ + pRec->nField = iCol; + assert( sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)<=0 + || pParse->db->mallocFailed ); + } + if( i>0 ){ + pRec->nField = nField; + assert( sqlite3VdbeRecordCompare(aSample[i-1].n, aSample[i-1].p, pRec)<0 + || pParse->db->mallocFailed ); + } + } } #endif /* ifdef SQLITE_DEBUG */ - /* At this point, aSample[i] is the first sample that is greater than - ** or equal to pVal. Or if i==pIdx->nSample, then all samples are less - ** than pVal. If aSample[i]==pVal, then res==0. - */ if( res==0 ){ + /* Record pRec is equal to sample i */ + assert( iCol==nField-1 ); aStat[0] = aSample[i].anLt[iCol]; aStat[1] = aSample[i].anEq[iCol]; }else{ - tRowcnt iLower, iUpper, iGap; - if( i==0 ){ - iLower = 0; - iUpper = aSample[0].anLt[iCol]; + /* At this point, the (iCol+1) field prefix of aSample[i] is the first + ** sample that is greater than pRec. Or, if i==pIdx->nSample then pRec + ** is larger than all samples in the array. */ + tRowcnt iUpper, iGap; + if( i>=pIdx->nSample ){ + iUpper = sqlite3LogEstToInt(pIdx->aiRowLogEst[0]); }else{ - i64 nRow0 = sqlite3LogEstToInt(pIdx->aiRowLogEst[0]); - iUpper = i>=pIdx->nSample ? nRow0 : aSample[i].anLt[iCol]; - iLower = aSample[i-1].anEq[iCol] + aSample[i-1].anLt[iCol]; + iUpper = aSample[i].anLt[iCol]; } - aStat[1] = pIdx->aAvgEq[iCol]; + if( iLower>=iUpper ){ iGap = 0; }else{ @@ -116924,7 +118380,11 @@ static int whereKeyStats( iGap = iGap/3; } aStat[0] = iLower + iGap; + aStat[1] = pIdx->aAvgEq[iCol]; } + + /* Restore the pRec->nField value before returning. */ + pRec->nField = nField; return i; } #endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */ @@ -117398,20 +118858,43 @@ static int whereInScanEst( ** but joins might run a little slower. The trick is to disable as much ** as we can without disabling too much. If we disabled in (1), we'd get ** the wrong answer. See ticket #813. +** +** If all the children of a term are disabled, then that term is also +** automatically disabled. In this way, terms get disabled if derived +** virtual terms are tested first. For example: +** +** x GLOB 'abc*' AND x>='abc' AND x<'acd' +** \___________/ \______/ \_____/ +** parent child1 child2 +** +** Only the parent term was in the original WHERE clause. The child1 +** and child2 terms were added by the LIKE optimization. If both of +** the virtual child terms are valid, then testing of the parent can be +** skipped. +** +** Usually the parent term is marked as TERM_CODED. But if the parent +** term was originally TERM_LIKE, then the parent gets TERM_LIKECOND instead. +** The TERM_LIKECOND marking indicates that the term should be coded inside +** a conditional such that is only evaluated on the second pass of a +** LIKE-optimization loop, when scanning BLOBs instead of strings. */ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){ - if( pTerm + int nLoop = 0; + while( pTerm && (pTerm->wtFlags & TERM_CODED)==0 && (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_FromJoin)) && (pLevel->notReady & pTerm->prereqAll)==0 ){ - pTerm->wtFlags |= TERM_CODED; - if( pTerm->iParent>=0 ){ - WhereTerm *pOther = &pTerm->pWC->a[pTerm->iParent]; - if( (--pOther->nChild)==0 ){ - disableTerm(pLevel, pOther); - } + if( nLoop && (pTerm->wtFlags & TERM_LIKE)!=0 ){ + pTerm->wtFlags |= TERM_LIKECOND; + }else{ + pTerm->wtFlags |= TERM_CODED; } + if( pTerm->iParent<0 ) break; + pTerm = &pTerm->pWC->a[pTerm->iParent]; + pTerm->nChild--; + if( pTerm->nChild!=0 ) break; + nLoop++; } } @@ -117790,8 +119273,7 @@ static int explainOneScan( || ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0)) || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX)); - sqlite3StrAccumInit(&str, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH); - str.db = db; + sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH); sqlite3StrAccumAppendAll(&str, isSearch ? "SEARCH" : "SCAN"); if( pItem->pSelect ){ sqlite3XPrintf(&str, 0, " SUBQUERY %d", pItem->iSelectId); @@ -117895,7 +119377,34 @@ static void addScanStatus( # define addScanStatus(a, b, c, d) ((void)d) #endif - +/* +** If the most recently coded instruction is a constant range contraint +** that originated from the LIKE optimization, then change the P3 to be +** pLoop->iLikeRepCntr and set P5. +** +** The LIKE optimization trys to evaluate "x LIKE 'abc%'" as a range +** expression: "x>='ABC' AND x<'abd'". But this requires that the range +** scan loop run twice, once for strings and a second time for BLOBs. +** The OP_String opcodes on the second pass convert the upper and lower +** bound string contants to blobs. This routine makes the necessary changes +** to the OP_String opcodes for that to happen. +*/ +static void whereLikeOptimizationStringFixup( + Vdbe *v, /* prepared statement under construction */ + WhereLevel *pLevel, /* The loop that contains the LIKE operator */ + WhereTerm *pTerm /* The upper or lower bound just coded */ +){ + if( pTerm->wtFlags & TERM_LIKEOPT ){ + VdbeOp *pOp; + assert( pLevel->iLikeRepCntr>0 ); + pOp = sqlite3VdbeGetOp(v, -1); + assert( pOp!=0 ); + assert( pOp->opcode==OP_String8 + || pTerm->pWC->pWInfo->pParse->db->mallocFailed ); + pOp->p3 = pLevel->iLikeRepCntr; + pOp->p5 = 1; + } +} /* ** Generate code for the start of the iLevel-th loop in the WHERE clause @@ -118225,10 +119734,25 @@ static Bitmask codeOneLoopStart( if( pLoop->wsFlags & WHERE_BTM_LIMIT ){ pRangeStart = pLoop->aLTerm[j++]; nExtraReg = 1; + /* Like optimization range constraints always occur in pairs */ + assert( (pRangeStart->wtFlags & TERM_LIKEOPT)==0 || + (pLoop->wsFlags & WHERE_TOP_LIMIT)!=0 ); } if( pLoop->wsFlags & WHERE_TOP_LIMIT ){ pRangeEnd = pLoop->aLTerm[j++]; nExtraReg = 1; + if( (pRangeEnd->wtFlags & TERM_LIKEOPT)!=0 ){ + assert( pRangeStart!=0 ); /* LIKE opt constraints */ + assert( pRangeStart->wtFlags & TERM_LIKEOPT ); /* occur in pairs */ + pLevel->iLikeRepCntr = ++pParse->nMem; + testcase( bRev ); + testcase( pIdx->aSortOrder[nEq]==SQLITE_SO_DESC ); + sqlite3VdbeAddOp2(v, OP_Integer, + bRev ^ (pIdx->aSortOrder[nEq]==SQLITE_SO_DESC), + pLevel->iLikeRepCntr); + VdbeComment((v, "LIKE loop counter")); + pLevel->addrLikeRep = sqlite3VdbeCurrentAddr(v); + } if( pRangeStart==0 && (j = pIdx->aiColumn[nEq])>=0 && pIdx->pTable->aCol[j].notNull==0 @@ -118271,6 +119795,7 @@ static Bitmask codeOneLoopStart( if( pRangeStart ){ Expr *pRight = pRangeStart->pExpr->pRight; sqlite3ExprCode(pParse, pRight, regBase+nEq); + whereLikeOptimizationStringFixup(v, pLevel, pRangeStart); if( (pRangeStart->wtFlags & TERM_VNULL)==0 && sqlite3ExprCanBeNull(pRight) ){ @@ -118316,6 +119841,7 @@ static Bitmask codeOneLoopStart( Expr *pRight = pRangeEnd->pExpr->pRight; sqlite3ExprCacheRemove(pParse, regBase+nEq, 1); sqlite3ExprCode(pParse, pRight, regBase+nEq); + whereLikeOptimizationStringFixup(v, pLevel, pRangeEnd); if( (pRangeEnd->wtFlags & TERM_VNULL)==0 && sqlite3ExprCanBeNull(pRight) ){ @@ -118543,7 +120069,8 @@ static Bitmask codeOneLoopStart( */ wctrlFlags = WHERE_OMIT_OPEN_CLOSE | WHERE_FORCE_TABLE - | WHERE_ONETABLE_ONLY; + | WHERE_ONETABLE_ONLY + | WHERE_NO_AUTOINDEX; for(ii=0; iinTerm; ii++){ WhereTerm *pOrTerm = &pOrWc->a[ii]; if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){ @@ -118705,6 +120232,7 @@ static Bitmask codeOneLoopStart( */ for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){ Expr *pE; + int skipLikeAddr = 0; testcase( pTerm->wtFlags & TERM_VIRTUAL ); testcase( pTerm->wtFlags & TERM_CODED ); if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; @@ -118719,7 +120247,13 @@ static Bitmask codeOneLoopStart( if( pLevel->iLeftJoin && !ExprHasProperty(pE, EP_FromJoin) ){ continue; } + if( pTerm->wtFlags & TERM_LIKECOND ){ + assert( pLevel->iLikeRepCntr>0 ); + skipLikeAddr = sqlite3VdbeAddOp1(v, OP_IfNot, pLevel->iLikeRepCntr); + VdbeCoverage(v); + } sqlite3ExprIfFalse(pParse, pE, addrCont, SQLITE_JUMPIFNULL); + if( skipLikeAddr ) sqlite3VdbeJumpHere(v, skipLikeAddr); pTerm->wtFlags |= TERM_CODED; } @@ -118938,6 +120472,13 @@ static void whereLoopDelete(sqlite3 *db, WhereLoop *p){ */ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){ if( ALWAYS(pWInfo) ){ + int i; + for(i=0; inLevel; i++){ + WhereLevel *pLevel = &pWInfo->a[i]; + if( pLevel->pWLoop && (pLevel->pWLoop->wsFlags & WHERE_IN_ABLE) ){ + sqlite3DbFree(db, pLevel->u.in.aInLoop); + } + } whereClauseClear(&pWInfo->sWC); while( pWInfo->pLoops ){ WhereLoop *p = pWInfo->pLoops; @@ -119384,6 +120925,10 @@ static int whereLoopAddBtreeIndex( } if( pTerm->prereqRight & pNew->maskSelf ) continue; + /* Do not allow the upper bound of a LIKE optimization range constraint + ** to mix with a lower range bound from some other source */ + if( pTerm->wtFlags & TERM_LIKEOPT && pTerm->eOperator==WO_LT ) continue; + pNew->wsFlags = saved_wsFlags; pNew->u.btree.nEq = saved_nEq; pNew->nLTerm = saved_nLTerm; @@ -119413,7 +120958,7 @@ static int whereLoopAddBtreeIndex( }else if( eOp & (WO_EQ) ){ pNew->wsFlags |= WHERE_COLUMN_EQ; if( iCol<0 || (nInMul==0 && pNew->u.btree.nEq==pProbe->nKeyCol-1) ){ - if( iCol>=0 && !IsUniqueIndex(pProbe) ){ + if( iCol>=0 && pProbe->uniqNotNull==0 ){ pNew->wsFlags |= WHERE_UNQ_WANTED; }else{ pNew->wsFlags |= WHERE_ONEROW; @@ -119427,6 +120972,17 @@ static int whereLoopAddBtreeIndex( pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT; pBtm = pTerm; pTop = 0; + if( pTerm->wtFlags & TERM_LIKEOPT ){ + /* Range contraints that come from the LIKE optimization are + ** always used in pairs. */ + pTop = &pTerm[1]; + assert( (pTop-(pTerm->pWC->a))pWC->nTerm ); + assert( pTop->wtFlags & TERM_LIKEOPT ); + assert( pTop->eOperator==WO_LT ); + if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */ + pNew->aLTerm[pNew->nLTerm++] = pTop; + pNew->wsFlags |= WHERE_TOP_LIMIT; + } }else{ assert( eOp & (WO_LT|WO_LE) ); testcase( eOp & WO_LT ); @@ -119628,7 +121184,12 @@ static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){ int i; WhereTerm *pTerm; for(i=0, pTerm=pWC->a; inTerm; i++, pTerm++){ - if( sqlite3ExprImpliesExpr(pTerm->pExpr, pWhere, iTab) ) return 1; + Expr *pExpr = pTerm->pExpr; + if( sqlite3ExprImpliesExpr(pExpr, pWhere, iTab) + && (!ExprHasProperty(pExpr, EP_FromJoin) || pExpr->iRightJoinTable==iTab) + ){ + return 1; + } } return 0; } @@ -119732,6 +121293,7 @@ static int whereLoopAddBtree( #ifndef SQLITE_OMIT_AUTOMATIC_INDEX /* Automatic indexes */ if( !pBuilder->pOrSet + && (pWInfo->wctrlFlags & WHERE_NO_AUTOINDEX)==0 && (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0 && pSrc->pIndex==0 && !pSrc->viaCoroutine @@ -120615,10 +122177,10 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ /* Seed the search with a single WherePath containing zero WhereLoops. ** - ** TUNING: Do not let the number of iterations go above 25. If the cost - ** of computing an automatic index is not paid back within the first 25 + ** TUNING: Do not let the number of iterations go above 28. If the cost + ** of computing an automatic index is not paid back within the first 28 ** rows, then do not use the automatic index. */ - aFrom[0].nRow = MIN(pParse->nQueryLoop, 46); assert( 46==sqlite3LogEst(25) ); + aFrom[0].nRow = MIN(pParse->nQueryLoop, 48); assert( 48==sqlite3LogEst(28) ); nFrom = 1; assert( aFrom[0].isOrdered==0 ); if( nOrderBy ){ @@ -120856,7 +122418,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ pWInfo->revMask = pFrom->revLoop; } if( (pWInfo->wctrlFlags & WHERE_SORTBYGROUP) - && pWInfo->nOBSat==pWInfo->pOrderBy->nExpr + && pWInfo->nOBSat==pWInfo->pOrderBy->nExpr && nLoop>0 ){ Bitmask revMask = 0; int nOrder = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pOrderBy, @@ -121261,7 +122823,6 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( } #ifdef WHERETRACE_ENABLED /* !=0 */ if( sqlite3WhereTrace ){ - int ii; sqlite3DebugPrintf("---- Solution nRow=%d", pWInfo->nRowOut); if( pWInfo->nOBSat>0 ){ sqlite3DebugPrintf(" ORDERBY=%d,0x%llx", pWInfo->nOBSat, pWInfo->revMask); @@ -121416,6 +122977,12 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( if( op ){ sqlite3VdbeAddOp3(v, op, iIndexCur, pIx->tnum, iDb); sqlite3VdbeSetP4KeyInfo(pParse, pIx); + if( (pLoop->wsFlags & WHERE_CONSTRAINT)!=0 + && (pLoop->wsFlags & (WHERE_COLUMN_RANGE|WHERE_SKIPSCAN))==0 + && (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)==0 + ){ + sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ); /* Hint to COMDB2 */ + } VdbeComment((v, "%s", pIx->zName)); } } @@ -121508,7 +123075,6 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ VdbeCoverageIf(v, pIn->eEndLoopOp==OP_NextIfOpen); sqlite3VdbeJumpHere(v, pIn->addrInTop-1); } - sqlite3DbFree(db, pLevel->u.in.aInLoop); } sqlite3VdbeResolveLabel(v, pLevel->addrBrk); if( pLevel->addrSkip ){ @@ -121517,6 +123083,16 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ sqlite3VdbeJumpHere(v, pLevel->addrSkip); sqlite3VdbeJumpHere(v, pLevel->addrSkip-2); } + if( pLevel->addrLikeRep ){ + int op; + if( sqlite3VdbeGetOp(v, pLevel->addrLikeRep-1)->p1 ){ + op = OP_DecrJumpZero; + }else{ + op = OP_JumpZeroIncr; + } + sqlite3VdbeAddOp2(v, op, pLevel->iLikeRepCntr, pLevel->addrLikeRep); + VdbeCoverage(v); + } if( pLevel->iLeftJoin ){ addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v); assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 @@ -121710,6 +123286,28 @@ struct TrigEvent { int a; IdList * b; }; struct AttachKey { int type; Token key; }; + /* + ** For a compound SELECT statement, make sure p->pPrior->pNext==p for + ** all elements in the list. And make sure list length does not exceed + ** SQLITE_LIMIT_COMPOUND_SELECT. + */ + static void parserDoubleLinkSelect(Parse *pParse, Select *p){ + if( p->pPrior ){ + Select *pNext = 0, *pLoop; + int mxSelect, cnt = 0; + for(pLoop=p; pLoop; pNext=pLoop, pLoop=pLoop->pPrior, cnt++){ + pLoop->pNext = pNext; + pLoop->selFlags |= SF_Compound; + } + if( (p->selFlags & SF_MultiValue)==0 && + (mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT])>0 && + cnt>mxSelect + ){ + sqlite3ErrorMsg(pParse, "too many terms in compound SELECT"); + } + } + } + /* This is a utility routine used to set the ExprSpan.zStart and ** ExprSpan.zEnd values of pOut so that the span covers the complete ** range of text beginning with pStart and going to the end of pEnd. @@ -124026,27 +125624,10 @@ static void yy_reduce( break; case 112: /* select ::= with selectnowith */ { - Select *p = yymsp[0].minor.yy3, *pNext, *pLoop; + Select *p = yymsp[0].minor.yy3; if( p ){ - int cnt = 0, mxSelect; p->pWith = yymsp[-1].minor.yy59; - if( p->pPrior ){ - u16 allValues = SF_Values; - pNext = 0; - for(pLoop=p; pLoop; pNext=pLoop, pLoop=pLoop->pPrior, cnt++){ - pLoop->pNext = pNext; - pLoop->selFlags |= SF_Compound; - allValues &= pLoop->selFlags; - } - if( allValues ){ - p->selFlags |= SF_AllValues; - }else if( - (mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT])>0 - && cnt>mxSelect - ){ - sqlite3ErrorMsg(pParse, "too many terms in compound SELECT"); - } - } + parserDoubleLinkSelect(pParse, p); }else{ sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy59); } @@ -124064,12 +125645,14 @@ static void yy_reduce( SrcList *pFrom; Token x; x.n = 0; + parserDoubleLinkSelect(pParse, pRhs); pFrom = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&x,pRhs,0,0); pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0,0); } if( pRhs ){ pRhs->op = (u8)yymsp[-1].minor.yy328; pRhs->pPrior = yymsp[-2].minor.yy3; + pRhs->selFlags &= ~SF_MultiValue; if( yymsp[-1].minor.yy328!=TK_ALL ) pParse->hasCompound = 1; }else{ sqlite3SelectDelete(pParse->db, yymsp[-2].minor.yy3); @@ -124116,13 +125699,16 @@ static void yy_reduce( break; case 121: /* values ::= values COMMA LP exprlist RP */ { - Select *pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy14,0,0,0,0,0,SF_Values,0,0); + Select *pRight, *pLeft = yymsp[-4].minor.yy3; + pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy14,0,0,0,0,0,SF_Values|SF_MultiValue,0,0); + if( ALWAYS(pLeft) ) pLeft->selFlags &= ~SF_MultiValue; if( pRight ){ pRight->op = TK_ALL; - pRight->pPrior = yymsp[-4].minor.yy3; + pLeft = yymsp[-4].minor.yy3; + pRight->pPrior = pLeft; yygotominor.yy3 = pRight; }else{ - yygotominor.yy3 = yymsp[-4].minor.yy3; + yygotominor.yy3 = pLeft; } } break; @@ -124407,7 +125993,7 @@ static void yy_reduce( break; case 193: /* expr ::= expr COLLATE ID|STRING */ { - yygotominor.yy346.pExpr = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy346.pExpr, &yymsp[0].minor.yy0); + yygotominor.yy346.pExpr = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy346.pExpr, &yymsp[0].minor.yy0, 1); yygotominor.yy346.zStart = yymsp[-2].minor.yy346.zStart; yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; } @@ -124570,7 +126156,7 @@ static void yy_reduce( yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy346.pExpr, 0, 0); if( yygotominor.yy346.pExpr ){ yygotominor.yy346.pExpr->x.pList = yymsp[-1].minor.yy14; - sqlite3ExprSetHeight(pParse, yygotominor.yy346.pExpr); + sqlite3ExprSetHeightAndFlags(pParse, yygotominor.yy346.pExpr); }else{ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14); } @@ -124585,8 +126171,8 @@ static void yy_reduce( yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_SELECT, 0, 0, 0); if( yygotominor.yy346.pExpr ){ yygotominor.yy346.pExpr->x.pSelect = yymsp[-1].minor.yy3; - ExprSetProperty(yygotominor.yy346.pExpr, EP_xIsSelect); - sqlite3ExprSetHeight(pParse, yygotominor.yy346.pExpr); + ExprSetProperty(yygotominor.yy346.pExpr, EP_xIsSelect|EP_Subquery); + sqlite3ExprSetHeightAndFlags(pParse, yygotominor.yy346.pExpr); }else{ sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy3); } @@ -124599,8 +126185,8 @@ static void yy_reduce( yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy346.pExpr, 0, 0); if( yygotominor.yy346.pExpr ){ yygotominor.yy346.pExpr->x.pSelect = yymsp[-1].minor.yy3; - ExprSetProperty(yygotominor.yy346.pExpr, EP_xIsSelect); - sqlite3ExprSetHeight(pParse, yygotominor.yy346.pExpr); + ExprSetProperty(yygotominor.yy346.pExpr, EP_xIsSelect|EP_Subquery); + sqlite3ExprSetHeightAndFlags(pParse, yygotominor.yy346.pExpr); }else{ sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy3); } @@ -124615,8 +126201,8 @@ static void yy_reduce( yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-3].minor.yy346.pExpr, 0, 0); if( yygotominor.yy346.pExpr ){ yygotominor.yy346.pExpr->x.pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0); - ExprSetProperty(yygotominor.yy346.pExpr, EP_xIsSelect); - sqlite3ExprSetHeight(pParse, yygotominor.yy346.pExpr); + ExprSetProperty(yygotominor.yy346.pExpr, EP_xIsSelect|EP_Subquery); + sqlite3ExprSetHeightAndFlags(pParse, yygotominor.yy346.pExpr); }else{ sqlite3SrcListDelete(pParse->db, pSrc); } @@ -124630,8 +126216,8 @@ static void yy_reduce( Expr *p = yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_EXISTS, 0, 0, 0); if( p ){ p->x.pSelect = yymsp[-1].minor.yy3; - ExprSetProperty(p, EP_xIsSelect); - sqlite3ExprSetHeight(pParse, p); + ExprSetProperty(p, EP_xIsSelect|EP_Subquery); + sqlite3ExprSetHeightAndFlags(pParse, p); }else{ sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy3); } @@ -124644,7 +126230,7 @@ static void yy_reduce( yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy132, 0, 0); if( yygotominor.yy346.pExpr ){ yygotominor.yy346.pExpr->x.pList = yymsp[-1].minor.yy132 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy14,yymsp[-1].minor.yy132) : yymsp[-2].minor.yy14; - sqlite3ExprSetHeight(pParse, yygotominor.yy346.pExpr); + sqlite3ExprSetHeightAndFlags(pParse, yygotominor.yy346.pExpr); }else{ sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy14); sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy132); @@ -124687,7 +126273,7 @@ static void yy_reduce( break; case 244: /* idxlist ::= idxlist COMMA nm collate sortorder */ { - Expr *p = sqlite3ExprAddCollateToken(pParse, 0, &yymsp[-1].minor.yy0); + Expr *p = sqlite3ExprAddCollateToken(pParse, 0, &yymsp[-1].minor.yy0, 1); yygotominor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, p); sqlite3ExprListSetName(pParse,yygotominor.yy14,&yymsp[-2].minor.yy0,1); sqlite3ExprListCheckLength(pParse, yygotominor.yy14, "index"); @@ -124696,7 +126282,7 @@ static void yy_reduce( break; case 245: /* idxlist ::= nm collate sortorder */ { - Expr *p = sqlite3ExprAddCollateToken(pParse, 0, &yymsp[-1].minor.yy0); + Expr *p = sqlite3ExprAddCollateToken(pParse, 0, &yymsp[-1].minor.yy0, 1); yygotominor.yy14 = sqlite3ExprListAppend(pParse,0, p); sqlite3ExprListSetName(pParse, yygotominor.yy14, &yymsp[-2].minor.yy0, 1); sqlite3ExprListCheckLength(pParse, yygotominor.yy14, "index"); @@ -125886,10 +127472,7 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr sqlite3 *db = pParse->db; /* The database connection */ int mxSqlLen; /* Max length of an SQL string */ - -#ifdef SQLITE_ENABLE_API_ARMOR - if( zSql==0 || pzErrMsg==0 ) return SQLITE_MISUSE_BKPT; -#endif + assert( zSql!=0 ); mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; if( db->nVdbeActive==0 ){ db->u1.isInterrupted = 0; @@ -125929,10 +127512,8 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr break; } case TK_ILLEGAL: { - sqlite3DbFree(db, *pzErrMsg); - *pzErrMsg = sqlite3MPrintf(db, "unrecognized token: \"%T\"", + sqlite3ErrorMsg(pParse, "unrecognized token: \"%T\"", &pParse->sLastToken); - nErr++; goto abort_parse; } case TK_SEMI: { @@ -125950,17 +127531,22 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr } } abort_parse: - if( zSql[i]==0 && nErr==0 && pParse->rc==SQLITE_OK ){ + assert( nErr==0 ); + if( zSql[i]==0 && pParse->rc==SQLITE_OK && db->mallocFailed==0 ){ if( lastTokenParsed!=TK_SEMI ){ sqlite3Parser(pEngine, TK_SEMI, pParse->sLastToken, pParse); pParse->zTail = &zSql[i]; } - sqlite3Parser(pEngine, 0, pParse->sLastToken, pParse); + if( pParse->rc==SQLITE_OK && db->mallocFailed==0 ){ + sqlite3Parser(pEngine, 0, pParse->sLastToken, pParse); + } } #ifdef YYTRACKMAXSTACKDEPTH + sqlite3_mutex_enter(sqlite3MallocMutex()); sqlite3StatusSet(SQLITE_STATUS_PARSER_STACK, sqlite3ParserStackPeak(pEngine) ); + sqlite3_mutex_leave(sqlite3MallocMutex()); #endif /* YYDEBUG */ sqlite3ParserFree(pEngine, sqlite3_free); db->lookaside.bEnabled = enableLookaside; @@ -126014,9 +127600,7 @@ abort_parse: pParse->pZombieTab = p->pNextZombie; sqlite3DeleteTable(db, p); } - if( nErr>0 && pParse->rc==SQLITE_OK ){ - pParse->rc = SQLITE_ERROR; - } + assert( nErr==0 || pParse->rc!=SQLITE_OK ); return nErr; } @@ -126124,7 +127708,7 @@ SQLITE_PRIVATE const char sqlite3IsEbcdicIdChar[]; ** to recognize the end of a trigger can be omitted. All we have to do ** is look for a semicolon that is not part of an string or comment. */ -SQLITE_API int sqlite3_complete(const char *zSql){ +SQLITE_API int SQLITE_STDCALL sqlite3_complete(const char *zSql){ u8 state = 0; /* Current state, using numbers defined in header comment */ u8 token; /* Value of the next token */ @@ -126289,10 +127873,10 @@ SQLITE_API int sqlite3_complete(const char *zSql){ ** above, except that the parameter is required to be UTF-16 encoded, not ** UTF-8. */ -SQLITE_API int sqlite3_complete16(const void *zSql){ +SQLITE_API int SQLITE_STDCALL sqlite3_complete16(const void *zSql){ sqlite3_value *pVal; char const *zSql8; - int rc = SQLITE_NOMEM; + int rc; #ifndef SQLITE_OMIT_AUTOINIT rc = sqlite3_initialize(); @@ -126439,24 +128023,36 @@ SQLITE_API const char sqlite3_version[] = SQLITE_VERSION; /* IMPLEMENTATION-OF: R-53536-42575 The sqlite3_libversion() function returns ** a pointer to the to the sqlite3_version[] string constant. */ -SQLITE_API const char *sqlite3_libversion(void){ return sqlite3_version; } +SQLITE_API const char *SQLITE_STDCALL sqlite3_libversion(void){ return sqlite3_version; } /* IMPLEMENTATION-OF: R-63124-39300 The sqlite3_sourceid() function returns a ** pointer to a string constant whose value is the same as the ** SQLITE_SOURCE_ID C preprocessor macro. */ -SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } +SQLITE_API const char *SQLITE_STDCALL sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } /* IMPLEMENTATION-OF: R-35210-63508 The sqlite3_libversion_number() function ** returns an integer equal to SQLITE_VERSION_NUMBER. */ -SQLITE_API int sqlite3_libversion_number(void){ return SQLITE_VERSION_NUMBER; } +SQLITE_API int SQLITE_STDCALL sqlite3_libversion_number(void){ return SQLITE_VERSION_NUMBER; } /* IMPLEMENTATION-OF: R-20790-14025 The sqlite3_threadsafe() function returns ** zero if and only if SQLite was compiled with mutexing code omitted due to ** the SQLITE_THREADSAFE compile-time option being set to 0. */ -SQLITE_API int sqlite3_threadsafe(void){ return SQLITE_THREADSAFE; } +SQLITE_API int SQLITE_STDCALL sqlite3_threadsafe(void){ return SQLITE_THREADSAFE; } + +/* +** When compiling the test fixture or with debugging enabled (on Win32), +** this variable being set to non-zero will cause OSTRACE macros to emit +** extra diagnostic information. +*/ +#ifdef SQLITE_HAVE_OS_TRACE +# ifndef SQLITE_DEBUG_OS_TRACE +# define SQLITE_DEBUG_OS_TRACE 0 +# endif + int sqlite3OSTrace = SQLITE_DEBUG_OS_TRACE; +#endif #if !defined(SQLITE_OMIT_TRACE) && defined(SQLITE_ENABLE_IOTRACE) /* @@ -126465,7 +128061,7 @@ SQLITE_API int sqlite3_threadsafe(void){ return SQLITE_THREADSAFE; } ** I/O active are written using this function. These messages ** are intended for debugging activity only. */ -/* not-private */ void (*sqlite3IoTrace)(const char*, ...) = 0; +SQLITE_API void (SQLITE_CDECL *sqlite3IoTrace)(const char*, ...) = 0; #endif /* @@ -126517,7 +128113,7 @@ SQLITE_API char *sqlite3_data_directory = 0; ** * Recursive calls to this routine from thread X return immediately ** without blocking. */ -SQLITE_API int sqlite3_initialize(void){ +SQLITE_API int SQLITE_STDCALL sqlite3_initialize(void){ MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */ int rc; /* Result code */ #ifdef SQLITE_EXTRA_INIT @@ -126531,6 +128127,11 @@ SQLITE_API int sqlite3_initialize(void){ } #endif + /* If the following assert() fails on some obscure processor/compiler + ** combination, the work-around is to set the correct pointer + ** size at compile-time using -DSQLITE_PTRSIZE=n compile-time option */ + assert( SQLITE_PTRSIZE==sizeof(char*) ); + /* If SQLite is already completely initialized, then this call ** to sqlite3_initialize() should be a no-op. But the initialization ** must be complete. So isInit must not be set until the very end @@ -126673,7 +128274,7 @@ SQLITE_API int sqlite3_initialize(void){ ** on when SQLite is already shut down. If SQLite is already shut down ** when this routine is invoked, then this routine is a harmless no-op. */ -SQLITE_API int sqlite3_shutdown(void){ +SQLITE_API int SQLITE_STDCALL sqlite3_shutdown(void){ #ifdef SQLITE_OMIT_WSD int rc = sqlite3_wsd_init(4096, 24); if( rc!=SQLITE_OK ){ @@ -126727,7 +128328,7 @@ SQLITE_API int sqlite3_shutdown(void){ ** threadsafe. Failure to heed these warnings can lead to unpredictable ** behavior. */ -SQLITE_API int sqlite3_config(int op, ...){ +SQLITE_API int SQLITE_CDECL sqlite3_config(int op, ...){ va_list ap; int rc = SQLITE_OK; @@ -126743,26 +128344,28 @@ SQLITE_API int sqlite3_config(int op, ...){ */ #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 /* IMP: R-54466-46756 */ case SQLITE_CONFIG_SINGLETHREAD: { - /* Disable all mutexing */ - sqlite3GlobalConfig.bCoreMutex = 0; - sqlite3GlobalConfig.bFullMutex = 0; + /* EVIDENCE-OF: R-02748-19096 This option sets the threading mode to + ** Single-thread. */ + sqlite3GlobalConfig.bCoreMutex = 0; /* Disable mutex on core */ + sqlite3GlobalConfig.bFullMutex = 0; /* Disable mutex on connections */ break; } #endif #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 /* IMP: R-20520-54086 */ case SQLITE_CONFIG_MULTITHREAD: { - /* Disable mutexing of database connections */ - /* Enable mutexing of core data structures */ - sqlite3GlobalConfig.bCoreMutex = 1; - sqlite3GlobalConfig.bFullMutex = 0; + /* EVIDENCE-OF: R-14374-42468 This option sets the threading mode to + ** Multi-thread. */ + sqlite3GlobalConfig.bCoreMutex = 1; /* Enable mutex on core */ + sqlite3GlobalConfig.bFullMutex = 0; /* Disable mutex on connections */ break; } #endif #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 /* IMP: R-59593-21810 */ case SQLITE_CONFIG_SERIALIZED: { - /* Enable all mutexing */ - sqlite3GlobalConfig.bCoreMutex = 1; - sqlite3GlobalConfig.bFullMutex = 1; + /* EVIDENCE-OF: R-41220-51800 This option sets the threading mode to + ** Serialized. */ + sqlite3GlobalConfig.bCoreMutex = 1; /* Enable mutex on core */ + sqlite3GlobalConfig.bFullMutex = 1; /* Enable mutex on connections */ break; } #endif @@ -126874,7 +128477,8 @@ SQLITE_API int sqlite3_config(int op, ...){ case SQLITE_CONFIG_HEAP: { /* EVIDENCE-OF: R-19854-42126 There are three arguments to ** SQLITE_CONFIG_HEAP: An 8-byte aligned pointer to the memory, the - ** number of bytes in the memory buffer, and the minimum allocation size. */ + ** number of bytes in the memory buffer, and the minimum allocation size. + */ sqlite3GlobalConfig.pHeap = va_arg(ap, void*); sqlite3GlobalConfig.nHeap = va_arg(ap, int); sqlite3GlobalConfig.mnReq = va_arg(ap, int); @@ -126979,7 +128583,9 @@ SQLITE_API int sqlite3_config(int op, ...){ ** compile-time maximum mmap size set by the SQLITE_MAX_MMAP_SIZE ** compile-time option. */ - if( mxMmap<0 || mxMmap>SQLITE_MAX_MMAP_SIZE ) mxMmap = SQLITE_MAX_MMAP_SIZE; + if( mxMmap<0 || mxMmap>SQLITE_MAX_MMAP_SIZE ){ + mxMmap = SQLITE_MAX_MMAP_SIZE; + } if( szMmap<0 ) szMmap = SQLITE_DEFAULT_MMAP_SIZE; if( szMmap>mxMmap) szMmap = mxMmap; sqlite3GlobalConfig.mxMmap = mxMmap; @@ -127079,7 +128685,7 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){ /* ** Return the mutex associated with a database connection. */ -SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3 *db){ +SQLITE_API sqlite3_mutex *SQLITE_STDCALL sqlite3_db_mutex(sqlite3 *db){ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; @@ -127093,7 +128699,7 @@ SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3 *db){ ** Free up as much memory as we can from the given database ** connection. */ -SQLITE_API int sqlite3_db_release_memory(sqlite3 *db){ +SQLITE_API int SQLITE_STDCALL sqlite3_db_release_memory(sqlite3 *db){ int i; #ifdef SQLITE_ENABLE_API_ARMOR @@ -127116,7 +128722,7 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3 *db){ /* ** Configuration settings for an individual database connection */ -SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ +SQLITE_API int SQLITE_CDECL sqlite3_db_config(sqlite3 *db, int op, ...){ va_list ap; int rc; va_start(ap, op); @@ -127235,7 +128841,7 @@ static int nocaseCollatingFunc( /* ** Return the ROWID of the most recent insert */ -SQLITE_API sqlite_int64 sqlite3_last_insert_rowid(sqlite3 *db){ +SQLITE_API sqlite_int64 SQLITE_STDCALL sqlite3_last_insert_rowid(sqlite3 *db){ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; @@ -127248,7 +128854,7 @@ SQLITE_API sqlite_int64 sqlite3_last_insert_rowid(sqlite3 *db){ /* ** Return the number of changes in the most recent call to sqlite3_exec(). */ -SQLITE_API int sqlite3_changes(sqlite3 *db){ +SQLITE_API int SQLITE_STDCALL sqlite3_changes(sqlite3 *db){ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; @@ -127261,7 +128867,7 @@ SQLITE_API int sqlite3_changes(sqlite3 *db){ /* ** Return the number of changes since the database handle was opened. */ -SQLITE_API int sqlite3_total_changes(sqlite3 *db){ +SQLITE_API int SQLITE_STDCALL sqlite3_total_changes(sqlite3 *db){ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; @@ -127403,8 +129009,8 @@ static int sqlite3Close(sqlite3 *db, int forceZombie){ ** unclosed resources, and arranges for deallocation when the last ** prepare statement or sqlite3_backup closes. */ -SQLITE_API int sqlite3_close(sqlite3 *db){ return sqlite3Close(db,0); } -SQLITE_API int sqlite3_close_v2(sqlite3 *db){ return sqlite3Close(db,1); } +SQLITE_API int SQLITE_STDCALL sqlite3_close(sqlite3 *db){ return sqlite3Close(db,0); } +SQLITE_API int SQLITE_STDCALL sqlite3_close_v2(sqlite3 *db){ return sqlite3Close(db,1); } /* @@ -127587,7 +129193,7 @@ SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3 *db, int tripCode){ ** Return a static string containing the name corresponding to the error code ** specified in the argument. */ -#if (defined(SQLITE_DEBUG) && SQLITE_OS_WIN) || defined(SQLITE_TEST) +#if defined(SQLITE_NEED_ERR_NAME) SQLITE_PRIVATE const char *sqlite3ErrName(int rc){ const char *zName = 0; int i, origRc = rc; @@ -127811,13 +129417,13 @@ SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler *p){ ** This routine sets the busy callback for an Sqlite database to the ** given callback function with the given argument. */ -SQLITE_API int sqlite3_busy_handler( +SQLITE_API int SQLITE_STDCALL sqlite3_busy_handler( sqlite3 *db, int (*xBusy)(void*,int), void *pArg ){ #ifdef SQLITE_ENABLE_API_ARMOR - if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE; + if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; #endif sqlite3_mutex_enter(db->mutex); db->busyHandler.xFunc = xBusy; @@ -127834,7 +129440,7 @@ SQLITE_API int sqlite3_busy_handler( ** given callback function with the given argument. The progress callback will ** be invoked every nOps opcodes. */ -SQLITE_API void sqlite3_progress_handler( +SQLITE_API void SQLITE_STDCALL sqlite3_progress_handler( sqlite3 *db, int nOps, int (*xProgress)(void*), @@ -127865,7 +129471,7 @@ SQLITE_API void sqlite3_progress_handler( ** This routine installs a default busy handler that waits for the ** specified number of milliseconds before returning 0. */ -SQLITE_API int sqlite3_busy_timeout(sqlite3 *db, int ms){ +SQLITE_API int SQLITE_STDCALL sqlite3_busy_timeout(sqlite3 *db, int ms){ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; #endif @@ -127881,7 +129487,7 @@ SQLITE_API int sqlite3_busy_timeout(sqlite3 *db, int ms){ /* ** Cause any pending operation to stop at its earliest opportunity. */ -SQLITE_API void sqlite3_interrupt(sqlite3 *db){ +SQLITE_API void SQLITE_STDCALL sqlite3_interrupt(sqlite3 *db){ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; @@ -127998,7 +129604,7 @@ SQLITE_PRIVATE int sqlite3CreateFunc( /* ** Create new user functions. */ -SQLITE_API int sqlite3_create_function( +SQLITE_API int SQLITE_STDCALL sqlite3_create_function( sqlite3 *db, const char *zFunc, int nArg, @@ -128012,7 +129618,7 @@ SQLITE_API int sqlite3_create_function( xFinal, 0); } -SQLITE_API int sqlite3_create_function_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_create_function_v2( sqlite3 *db, const char *zFunc, int nArg, @@ -128055,7 +129661,7 @@ SQLITE_API int sqlite3_create_function_v2( } #ifndef SQLITE_OMIT_UTF16 -SQLITE_API int sqlite3_create_function16( +SQLITE_API int SQLITE_STDCALL sqlite3_create_function16( sqlite3 *db, const void *zFunctionName, int nArg, @@ -128095,7 +129701,7 @@ SQLITE_API int sqlite3_create_function16( ** A global function must exist in order for name resolution to work ** properly. */ -SQLITE_API int sqlite3_overload_function( +SQLITE_API int SQLITE_STDCALL sqlite3_overload_function( sqlite3 *db, const char *zName, int nArg @@ -128127,7 +129733,7 @@ SQLITE_API int sqlite3_overload_function( ** trace is a pointer to a function that is invoked at the start of each ** SQL statement. */ -SQLITE_API void *sqlite3_trace(sqlite3 *db, void (*xTrace)(void*,const char*), void *pArg){ +SQLITE_API void *SQLITE_STDCALL sqlite3_trace(sqlite3 *db, void (*xTrace)(void*,const char*), void *pArg){ void *pOld; #ifdef SQLITE_ENABLE_API_ARMOR @@ -128151,7 +129757,7 @@ SQLITE_API void *sqlite3_trace(sqlite3 *db, void (*xTrace)(void*,const char*), v ** profile is a pointer to a function that is invoked at the conclusion of ** each SQL statement that is run. */ -SQLITE_API void *sqlite3_profile( +SQLITE_API void *SQLITE_STDCALL sqlite3_profile( sqlite3 *db, void (*xProfile)(void*,const char*,sqlite_uint64), void *pArg @@ -128178,7 +129784,7 @@ SQLITE_API void *sqlite3_profile( ** If the invoked function returns non-zero, then the commit becomes a ** rollback. */ -SQLITE_API void *sqlite3_commit_hook( +SQLITE_API void *SQLITE_STDCALL sqlite3_commit_hook( sqlite3 *db, /* Attach the hook to this database */ int (*xCallback)(void*), /* Function to invoke on each commit */ void *pArg /* Argument to the function */ @@ -128203,7 +129809,7 @@ SQLITE_API void *sqlite3_commit_hook( ** Register a callback to be invoked each time a row is updated, ** inserted or deleted using this database connection. */ -SQLITE_API void *sqlite3_update_hook( +SQLITE_API void *SQLITE_STDCALL sqlite3_update_hook( sqlite3 *db, /* Attach the hook to this database */ void (*xCallback)(void*,int,char const *,char const *,sqlite_int64), void *pArg /* Argument to the function */ @@ -128228,7 +129834,7 @@ SQLITE_API void *sqlite3_update_hook( ** Register a callback to be invoked each time a transaction is rolled ** back by this database connection. */ -SQLITE_API void *sqlite3_rollback_hook( +SQLITE_API void *SQLITE_STDCALL sqlite3_rollback_hook( sqlite3 *db, /* Attach the hook to this database */ void (*xCallback)(void*), /* Callback function */ void *pArg /* Argument to the function */ @@ -128282,7 +129888,7 @@ SQLITE_PRIVATE int sqlite3WalDefaultHook( ** using sqlite3_wal_hook() disables the automatic checkpoint mechanism ** configured by this function. */ -SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int nFrame){ +SQLITE_API int SQLITE_STDCALL sqlite3_wal_autocheckpoint(sqlite3 *db, int nFrame){ #ifdef SQLITE_OMIT_WAL UNUSED_PARAMETER(db); UNUSED_PARAMETER(nFrame); @@ -128303,7 +129909,7 @@ SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int nFrame){ ** Register a callback to be invoked each time a transaction is written ** into the write-ahead-log by this database connection. */ -SQLITE_API void *sqlite3_wal_hook( +SQLITE_API void *SQLITE_STDCALL sqlite3_wal_hook( sqlite3 *db, /* Attach the hook to this db handle */ int(*xCallback)(void *, sqlite3*, const char*, int), void *pArg /* First argument passed to xCallback() */ @@ -128330,7 +129936,7 @@ SQLITE_API void *sqlite3_wal_hook( /* ** Checkpoint database zDb. */ -SQLITE_API int sqlite3_wal_checkpoint_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_wal_checkpoint_v2( sqlite3 *db, /* Database handle */ const char *zDb, /* Name of attached database (or NULL) */ int eMode, /* SQLITE_CHECKPOINT_* value */ @@ -128369,6 +129975,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2( rc = SQLITE_ERROR; sqlite3ErrorWithMsg(db, SQLITE_ERROR, "unknown database: %s", zDb); }else{ + db->busyHandler.nBusy = 0; rc = sqlite3Checkpoint(db, iDb, eMode, pnLog, pnCkpt); sqlite3Error(db, rc); } @@ -128384,7 +129991,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2( ** to contains a zero-length string, all attached databases are ** checkpointed. */ -SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){ +SQLITE_API int SQLITE_STDCALL sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){ /* EVIDENCE-OF: R-41613-20553 The sqlite3_wal_checkpoint(D,X) is equivalent to ** sqlite3_wal_checkpoint_v2(D,X,SQLITE_CHECKPOINT_PASSIVE,0,0). */ return sqlite3_wal_checkpoint_v2(db,zDb,SQLITE_CHECKPOINT_PASSIVE,0,0); @@ -128473,7 +130080,7 @@ SQLITE_PRIVATE int sqlite3TempInMemory(const sqlite3 *db){ ** Return UTF-8 encoded English language explanation of the most recent ** error. */ -SQLITE_API const char *sqlite3_errmsg(sqlite3 *db){ +SQLITE_API const char *SQLITE_STDCALL sqlite3_errmsg(sqlite3 *db){ const char *z; if( !db ){ return sqlite3ErrStr(SQLITE_NOMEM); @@ -128501,7 +130108,7 @@ SQLITE_API const char *sqlite3_errmsg(sqlite3 *db){ ** Return UTF-16 encoded English language explanation of the most recent ** error. */ -SQLITE_API const void *sqlite3_errmsg16(sqlite3 *db){ +SQLITE_API const void *SQLITE_STDCALL sqlite3_errmsg16(sqlite3 *db){ static const u16 outOfMem[] = { 'o', 'u', 't', ' ', 'o', 'f', ' ', 'm', 'e', 'm', 'o', 'r', 'y', 0 }; @@ -128546,7 +130153,7 @@ SQLITE_API const void *sqlite3_errmsg16(sqlite3 *db){ ** Return the most recent error code generated by an SQLite routine. If NULL is ** passed to this function, we assume a malloc() failed during sqlite3_open(). */ -SQLITE_API int sqlite3_errcode(sqlite3 *db){ +SQLITE_API int SQLITE_STDCALL sqlite3_errcode(sqlite3 *db){ if( db && !sqlite3SafetyCheckSickOrOk(db) ){ return SQLITE_MISUSE_BKPT; } @@ -128555,7 +130162,7 @@ SQLITE_API int sqlite3_errcode(sqlite3 *db){ } return db->errCode & db->errMask; } -SQLITE_API int sqlite3_extended_errcode(sqlite3 *db){ +SQLITE_API int SQLITE_STDCALL sqlite3_extended_errcode(sqlite3 *db){ if( db && !sqlite3SafetyCheckSickOrOk(db) ){ return SQLITE_MISUSE_BKPT; } @@ -128570,7 +130177,7 @@ SQLITE_API int sqlite3_extended_errcode(sqlite3 *db){ ** argument. For now, this simply calls the internal sqlite3ErrStr() ** function. */ -SQLITE_API const char *sqlite3_errstr(int rc){ +SQLITE_API const char *SQLITE_STDCALL sqlite3_errstr(int rc){ return sqlite3ErrStr(rc); } @@ -128718,7 +130325,7 @@ static const int aHardLimit[] = { ** It merely prevents new constructs that exceed the limit ** from forming. */ -SQLITE_API int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ +SQLITE_API int SQLITE_STDCALL sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ int oldLimit; #ifdef SQLITE_ENABLE_API_ARMOR @@ -128811,18 +130418,30 @@ SQLITE_PRIVATE int sqlite3ParseUri( int eState; /* Parser state when parsing URI */ int iIn; /* Input character index */ int iOut = 0; /* Output character index */ - int nByte = nUri+2; /* Bytes of space to allocate */ + u64 nByte = nUri+2; /* Bytes of space to allocate */ /* Make sure the SQLITE_OPEN_URI flag is set to indicate to the VFS xOpen ** method that there may be extra parameters following the file-name. */ flags |= SQLITE_OPEN_URI; for(iIn=0; iInmallocFailed && rc==SQLITE_OK){ + int sqlite3_dbstat_register(sqlite3*); + rc = sqlite3_dbstat_register(db); + } +#endif + /* -DSQLITE_DEFAULT_LOCKING_MODE=1 makes EXCLUSIVE the default locking ** mode. -DSQLITE_DEFAULT_LOCKING_MODE=0 make NORMAL the default locking ** mode. Doing nothing at all also makes NORMAL the default. @@ -129262,7 +130891,8 @@ static int openDatabase( opendb_out: sqlite3_free(zOpen); if( db ){ - assert( db->mutex!=0 || isThreadsafe==0 || sqlite3GlobalConfig.bFullMutex==0 ); + assert( db->mutex!=0 || isThreadsafe==0 + || sqlite3GlobalConfig.bFullMutex==0 ); sqlite3_mutex_leave(db->mutex); } rc = sqlite3_errcode(db); @@ -129287,14 +130917,14 @@ opendb_out: /* ** Open a new database handle. */ -SQLITE_API int sqlite3_open( +SQLITE_API int SQLITE_STDCALL sqlite3_open( const char *zFilename, sqlite3 **ppDb ){ return openDatabase(zFilename, ppDb, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0); } -SQLITE_API int sqlite3_open_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_open_v2( const char *filename, /* Database filename (UTF-8) */ sqlite3 **ppDb, /* OUT: SQLite db handle */ int flags, /* Flags */ @@ -129307,7 +130937,7 @@ SQLITE_API int sqlite3_open_v2( /* ** Open a new database handle. */ -SQLITE_API int sqlite3_open16( +SQLITE_API int SQLITE_STDCALL sqlite3_open16( const void *zFilename, sqlite3 **ppDb ){ @@ -129346,7 +130976,7 @@ SQLITE_API int sqlite3_open16( /* ** Register a new collation sequence with the database handle db. */ -SQLITE_API int sqlite3_create_collation( +SQLITE_API int SQLITE_STDCALL sqlite3_create_collation( sqlite3* db, const char *zName, int enc, @@ -129359,7 +130989,7 @@ SQLITE_API int sqlite3_create_collation( /* ** Register a new collation sequence with the database handle db. */ -SQLITE_API int sqlite3_create_collation_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_create_collation_v2( sqlite3* db, const char *zName, int enc, @@ -129384,7 +131014,7 @@ SQLITE_API int sqlite3_create_collation_v2( /* ** Register a new collation sequence with the database handle db. */ -SQLITE_API int sqlite3_create_collation16( +SQLITE_API int SQLITE_STDCALL sqlite3_create_collation16( sqlite3* db, const void *zName, int enc, @@ -129414,7 +131044,7 @@ SQLITE_API int sqlite3_create_collation16( ** Register a collation sequence factory callback with the database handle ** db. Replace any previously installed collation sequence factory. */ -SQLITE_API int sqlite3_collation_needed( +SQLITE_API int SQLITE_STDCALL sqlite3_collation_needed( sqlite3 *db, void *pCollNeededArg, void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*) @@ -129435,7 +131065,7 @@ SQLITE_API int sqlite3_collation_needed( ** Register a collation sequence factory callback with the database handle ** db. Replace any previously installed collation sequence factory. */ -SQLITE_API int sqlite3_collation_needed16( +SQLITE_API int SQLITE_STDCALL sqlite3_collation_needed16( sqlite3 *db, void *pCollNeededArg, void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*) @@ -129457,7 +131087,7 @@ SQLITE_API int sqlite3_collation_needed16( ** This function is now an anachronism. It used to be used to recover from a ** malloc() failure, but SQLite now does this automatically. */ -SQLITE_API int sqlite3_global_recover(void){ +SQLITE_API int SQLITE_STDCALL sqlite3_global_recover(void){ return SQLITE_OK; } #endif @@ -129468,7 +131098,7 @@ SQLITE_API int sqlite3_global_recover(void){ ** by default. Autocommit is disabled by a BEGIN statement and reenabled ** by the next COMMIT or ROLLBACK. */ -SQLITE_API int sqlite3_get_autocommit(sqlite3 *db){ +SQLITE_API int SQLITE_STDCALL sqlite3_get_autocommit(sqlite3 *db){ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; @@ -129520,7 +131150,7 @@ SQLITE_PRIVATE int sqlite3CantopenError(int lineno){ ** SQLite no longer uses thread-specific data so this routine is now a ** no-op. It is retained for historical compatibility. */ -SQLITE_API void sqlite3_thread_cleanup(void){ +SQLITE_API void SQLITE_STDCALL sqlite3_thread_cleanup(void){ } #endif @@ -129528,7 +131158,7 @@ SQLITE_API void sqlite3_thread_cleanup(void){ ** Return meta information about a specific column of a database table. ** See comment in sqlite3.h (sqlite.h.in) for details. */ -SQLITE_API int sqlite3_table_column_metadata( +SQLITE_API int SQLITE_STDCALL sqlite3_table_column_metadata( sqlite3 *db, /* Connection handle */ const char *zDbName, /* Database name or NULL */ const char *zTableName, /* Table name */ @@ -129544,13 +131174,19 @@ SQLITE_API int sqlite3_table_column_metadata( Table *pTab = 0; Column *pCol = 0; int iCol = 0; - char const *zDataType = 0; char const *zCollSeq = 0; int notnull = 0; int primarykey = 0; int autoinc = 0; + +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) || zTableName==0 ){ + return SQLITE_MISUSE_BKPT; + } +#endif + /* Ensure the database schema has been loaded */ sqlite3_mutex_enter(db->mutex); sqlite3BtreeEnterAll(db); @@ -129640,7 +131276,7 @@ error_out: /* ** Sleep for a little while. Return the amount of time slept. */ -SQLITE_API int sqlite3_sleep(int ms){ +SQLITE_API int SQLITE_STDCALL sqlite3_sleep(int ms){ sqlite3_vfs *pVfs; int rc; pVfs = sqlite3_vfs_find(0); @@ -129656,7 +131292,7 @@ SQLITE_API int sqlite3_sleep(int ms){ /* ** Enable or disable the extended result codes. */ -SQLITE_API int sqlite3_extended_result_codes(sqlite3 *db, int onoff){ +SQLITE_API int SQLITE_STDCALL sqlite3_extended_result_codes(sqlite3 *db, int onoff){ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; #endif @@ -129669,7 +131305,7 @@ SQLITE_API int sqlite3_extended_result_codes(sqlite3 *db, int onoff){ /* ** Invoke the xFileControl method on a particular database. */ -SQLITE_API int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, void *pArg){ +SQLITE_API int SQLITE_STDCALL sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, void *pArg){ int rc = SQLITE_ERROR; Btree *pBtree; @@ -129697,13 +131333,13 @@ SQLITE_API int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, vo sqlite3BtreeLeave(pBtree); } sqlite3_mutex_leave(db->mutex); - return rc; + return rc; } /* ** Interface to the testing logic. */ -SQLITE_API int sqlite3_test_control(int op, ...){ +SQLITE_API int SQLITE_CDECL sqlite3_test_control(int op, ...){ int rc = 0; #ifndef SQLITE_OMIT_BUILTIN_TEST va_list ap; @@ -130000,6 +131636,35 @@ SQLITE_API int sqlite3_test_control(int op, ...){ if( sqlite3GlobalConfig.isInit==0 ) rc = SQLITE_ERROR; break; } + + /* sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, dbName, onOff, tnum); + ** + ** This test control is used to create imposter tables. "db" is a pointer + ** to the database connection. dbName is the database name (ex: "main" or + ** "temp") which will receive the imposter. "onOff" turns imposter mode on + ** or off. "tnum" is the root page of the b-tree to which the imposter + ** table should connect. + ** + ** Enable imposter mode only when the schema has already been parsed. Then + ** run a single CREATE TABLE statement to construct the imposter table in + ** the parsed schema. Then turn imposter mode back off again. + ** + ** If onOff==0 and tnum>0 then reset the schema for all databases, causing + ** the schema to be reparsed the next time it is needed. This has the + ** effect of erasing all imposter tables. + */ + case SQLITE_TESTCTRL_IMPOSTER: { + sqlite3 *db = va_arg(ap, sqlite3*); + sqlite3_mutex_enter(db->mutex); + db->init.iDb = sqlite3FindDbName(db, va_arg(ap,const char*)); + db->init.busy = db->init.imposterTable = va_arg(ap,int); + db->init.newTnum = va_arg(ap,int); + if( db->init.busy==0 && db->init.newTnum>0 ){ + sqlite3ResetAllSchemasOfConnection(db); + } + sqlite3_mutex_leave(db->mutex); + break; + } } va_end(ap); #endif /* SQLITE_OMIT_BUILTIN_TEST */ @@ -130017,7 +131682,7 @@ SQLITE_API int sqlite3_test_control(int op, ...){ ** parameter if it exists. If the parameter does not exist, this routine ** returns a NULL pointer. */ -SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam){ +SQLITE_API const char *SQLITE_STDCALL sqlite3_uri_parameter(const char *zFilename, const char *zParam){ if( zFilename==0 || zParam==0 ) return 0; zFilename += sqlite3Strlen30(zFilename) + 1; while( zFilename[0] ){ @@ -130032,7 +131697,7 @@ SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char * /* ** Return a boolean value for a query parameter. */ -SQLITE_API int sqlite3_uri_boolean(const char *zFilename, const char *zParam, int bDflt){ +SQLITE_API int SQLITE_STDCALL sqlite3_uri_boolean(const char *zFilename, const char *zParam, int bDflt){ const char *z = sqlite3_uri_parameter(zFilename, zParam); bDflt = bDflt!=0; return z ? sqlite3GetBoolean(z, bDflt) : bDflt; @@ -130041,7 +131706,7 @@ SQLITE_API int sqlite3_uri_boolean(const char *zFilename, const char *zParam, in /* ** Return a 64-bit integer value for a query parameter. */ -SQLITE_API sqlite3_int64 sqlite3_uri_int64( +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_uri_int64( const char *zFilename, /* Filename as passed to xOpen */ const char *zParam, /* URI parameter sought */ sqlite3_int64 bDflt /* return if parameter is missing */ @@ -130073,7 +131738,7 @@ SQLITE_PRIVATE Btree *sqlite3DbNameToBtree(sqlite3 *db, const char *zDbName){ ** Return the filename of the database associated with a database ** connection. */ -SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){ +SQLITE_API const char *SQLITE_STDCALL sqlite3_db_filename(sqlite3 *db, const char *zDbName){ Btree *pBt; #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ @@ -130089,7 +131754,7 @@ SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){ ** Return 1 if database is read-only or 0 if read/write. Return -1 if ** no such database exists. */ -SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName){ +SQLITE_API int SQLITE_STDCALL sqlite3_db_readonly(sqlite3 *db, const char *zDbName){ Btree *pBt; #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ @@ -130248,7 +131913,7 @@ static void leaveMutex(void){ ** on the same "db". If xNotify==0 then any prior callbacks are immediately ** cancelled. */ -SQLITE_API int sqlite3_unlock_notify( +SQLITE_API int SQLITE_STDCALL sqlite3_unlock_notify( sqlite3 *db, void (*xNotify)(void **, int), void *pArg @@ -131142,6 +132807,11 @@ SQLITE_PRIVATE Fts3HashElem *sqlite3Fts3HashFindElem(const Fts3Hash *, const voi #ifdef SQLITE_COVERAGE_TEST # define ALWAYS(x) (1) # define NEVER(X) (0) +#elif defined(SQLITE_DEBUG) +# define ALWAYS(x) sqlite3Fts3Always((x)!=0) +# define NEVER(x) sqlite3Fts3Never((x)!=0) +SQLITE_PRIVATE int sqlite3Fts3Always(int b); +SQLITE_PRIVATE int sqlite3Fts3Never(int b); #else # define ALWAYS(x) (x) # define NEVER(x) (x) @@ -131383,6 +133053,11 @@ struct Fts3Phrase { int bIncr; /* True if doclist is loaded incrementally */ int iDoclistToken; + /* Used by sqlite3Fts3EvalPhrasePoslist() if this is a descendent of an + ** OR condition. */ + char *pOrPoslist; + i64 iOrDocid; + /* Variables below this point are populated by fts3_expr.c when parsing ** a MATCH expression. Everything above is part of the evaluation phase. */ @@ -131537,6 +133212,7 @@ SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table*,int,int); ) /* fts3.c */ +SQLITE_PRIVATE void sqlite3Fts3ErrMsg(char**,const char*,...); SQLITE_PRIVATE int sqlite3Fts3PutVarint(char *, sqlite3_int64); SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *, sqlite_int64 *); SQLITE_PRIVATE int sqlite3Fts3GetVarint32(const char *, int *); @@ -131626,6 +133302,13 @@ static int fts3EvalStart(Fts3Cursor *pCsr); static int fts3TermSegReaderCursor( Fts3Cursor *, const char *, int, int, Fts3MultiSegReader **); +#ifndef SQLITE_AMALGAMATION +# if defined(SQLITE_DEBUG) +SQLITE_PRIVATE int sqlite3Fts3Always(int b) { assert( b ); return b; } +SQLITE_PRIVATE int sqlite3Fts3Never(int b) { assert( !b ); return b; } +# endif +#endif + /* ** Write a 64-bit variable-length integer to memory starting at p[0]. ** The length of data written will be between 1 and FTS3_VARINT_MAX bytes. @@ -131735,7 +133418,7 @@ SQLITE_PRIVATE void sqlite3Fts3Dequote(char *z){ /* If the first byte was a '[', then the close-quote character is a ']' */ if( quote=='[' ) quote = ']'; - while( ALWAYS(z[iIn]) ){ + while( z[iIn] ){ if( z[iIn]==quote ){ if( z[iIn+1]!=quote ) break; z[iOut++] = quote; @@ -131814,6 +133497,17 @@ static int fts3DisconnectMethod(sqlite3_vtab *pVtab){ return SQLITE_OK; } +/* +** Write an error message into *pzErr +*/ +SQLITE_PRIVATE void sqlite3Fts3ErrMsg(char **pzErr, const char *zFormat, ...){ + va_list ap; + sqlite3_free(*pzErr); + va_start(ap, zFormat); + *pzErr = sqlite3_vmprintf(zFormat, ap); + va_end(ap); +} + /* ** Construct one or more SQL statements from the format string given ** and then evaluate those statements. The success code is written @@ -132223,11 +133917,16 @@ static char *fts3WriteExprList(Fts3Table *p, const char *zFunc, int *pRc){ ** This function is used when parsing the "prefix=" FTS4 parameter. */ static int fts3GobbleInt(const char **pp, int *pnOut){ + const int MAX_NPREFIX = 10000000; const char *p; /* Iterator pointer */ int nInt = 0; /* Output value */ for(p=*pp; p[0]>='0' && p[0]<='9'; p++){ nInt = nInt * 10 + (p[0] - '0'); + if( nInt>MAX_NPREFIX ){ + nInt = 0; + break; + } } if( p==*pp ) return SQLITE_ERROR; *pnOut = nInt; @@ -132270,7 +133969,6 @@ static int fts3PrefixParameter( aIndex = sqlite3_malloc(sizeof(struct Fts3Index) * nIndex); *apIndex = aIndex; - *pnIndex = nIndex; if( !aIndex ){ return SQLITE_NOMEM; } @@ -132280,13 +133978,20 @@ static int fts3PrefixParameter( const char *p = zParam; int i; for(i=1; i=0 ); + if( nPrefix==0 ){ + nIndex--; + i--; + }else{ + aIndex[i].nPrefix = nPrefix; + } p++; } } + *pnIndex = nIndex; return SQLITE_OK; } @@ -132321,7 +134026,8 @@ static int fts3ContentColumns( const char *zTbl, /* Name of content table */ const char ***pazCol, /* OUT: Malloc'd array of column names */ int *pnCol, /* OUT: Size of array *pazCol */ - int *pnStr /* OUT: Bytes of string content */ + int *pnStr, /* OUT: Bytes of string content */ + char **pzErr /* OUT: error message */ ){ int rc = SQLITE_OK; /* Return code */ char *zSql; /* "SELECT *" statement on zTbl */ @@ -132332,6 +134038,9 @@ static int fts3ContentColumns( rc = SQLITE_NOMEM; }else{ rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); + if( rc!=SQLITE_OK ){ + sqlite3Fts3ErrMsg(pzErr, "%s", sqlite3_errmsg(db)); + } } sqlite3_free(zSql); @@ -132410,7 +134119,7 @@ static int fts3InitVtab( const char **aCol; /* Array of column names */ sqlite3_tokenizer *pTokenizer = 0; /* Tokenizer for this table */ - int nIndex; /* Size of aIndex[] array */ + int nIndex = 0; /* Size of aIndex[] array */ struct Fts3Index *aIndex = 0; /* Array of indexes for this table */ /* The results of parsing supported FTS4 key=value options: */ @@ -132498,13 +134207,13 @@ static int fts3InitVtab( } } if( iOpt==SizeofArray(aFts4Opt) ){ - *pzErr = sqlite3_mprintf("unrecognized parameter: %s", z); + sqlite3Fts3ErrMsg(pzErr, "unrecognized parameter: %s", z); rc = SQLITE_ERROR; }else{ switch( iOpt ){ case 0: /* MATCHINFO */ if( strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "fts3", 4) ){ - *pzErr = sqlite3_mprintf("unrecognized matchinfo: %s", zVal); + sqlite3Fts3ErrMsg(pzErr, "unrecognized matchinfo: %s", zVal); rc = SQLITE_ERROR; } bNoDocsize = 1; @@ -132532,7 +134241,7 @@ static int fts3InitVtab( if( (strlen(zVal)!=3 || sqlite3_strnicmp(zVal, "asc", 3)) && (strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "desc", 4)) ){ - *pzErr = sqlite3_mprintf("unrecognized order: %s", zVal); + sqlite3Fts3ErrMsg(pzErr, "unrecognized order: %s", zVal); rc = SQLITE_ERROR; } bDescIdx = (zVal[0]=='d' || zVal[0]=='D'); @@ -132583,7 +134292,7 @@ static int fts3InitVtab( if( nCol==0 ){ sqlite3_free((void*)aCol); aCol = 0; - rc = fts3ContentColumns(db, argv[1], zContent, &aCol, &nCol, &nString); + rc = fts3ContentColumns(db, argv[1], zContent,&aCol,&nCol,&nString,pzErr); /* If a languageid= option was specified, remove the language id ** column from the aCol[] array. */ @@ -132618,7 +134327,7 @@ static int fts3InitVtab( rc = fts3PrefixParameter(zPrefix, &nIndex, &aIndex); if( rc==SQLITE_ERROR ){ assert( zPrefix ); - *pzErr = sqlite3_mprintf("error parsing prefix parameter: %s", zPrefix); + sqlite3Fts3ErrMsg(pzErr, "error parsing prefix parameter: %s", zPrefix); } if( rc!=SQLITE_OK ) goto fts3_init_out; @@ -132700,7 +134409,7 @@ static int fts3InitVtab( } for(i=0; izReadExprlist = fts3ReadExprList(p, zUncompress, &rc); p->zWriteExprlist = fts3WriteExprList(p, zCompress, &rc); @@ -133804,26 +135513,33 @@ static int fts3DoclistOrMerge( ** ** The right-hand input doclist is overwritten by this function. */ -static void fts3DoclistPhraseMerge( +static int fts3DoclistPhraseMerge( int bDescDoclist, /* True if arguments are desc */ int nDist, /* Distance from left to right (1=adjacent) */ char *aLeft, int nLeft, /* Left doclist */ - char *aRight, int *pnRight /* IN/OUT: Right/output doclist */ + char **paRight, int *pnRight /* IN/OUT: Right/output doclist */ ){ sqlite3_int64 i1 = 0; sqlite3_int64 i2 = 0; sqlite3_int64 iPrev = 0; + char *aRight = *paRight; char *pEnd1 = &aLeft[nLeft]; char *pEnd2 = &aRight[*pnRight]; char *p1 = aLeft; char *p2 = aRight; char *p; int bFirstOut = 0; - char *aOut = aRight; + char *aOut; assert( nDist>0 ); - + if( bDescDoclist ){ + aOut = sqlite3_malloc(*pnRight + FTS3_VARINT_MAX); + if( aOut==0 ) return SQLITE_NOMEM; + }else{ + aOut = aRight; + } p = aOut; + fts3GetDeltaVarint3(&p1, pEnd1, 0, &i1); fts3GetDeltaVarint3(&p2, pEnd2, 0, &i2); @@ -133852,6 +135568,12 @@ static void fts3DoclistPhraseMerge( } *pnRight = (int)(p - aOut); + if( bDescDoclist ){ + sqlite3_free(aRight); + *paRight = aOut; + } + + return SQLITE_OK; } /* @@ -133976,8 +135698,22 @@ static int fts3TermSelectMerge( ){ if( pTS->aaOutput[0]==0 ){ /* If this is the first term selected, copy the doclist to the output - ** buffer using memcpy(). */ - pTS->aaOutput[0] = sqlite3_malloc(nDoclist); + ** buffer using memcpy(). + ** + ** Add FTS3_VARINT_MAX bytes of unused space to the end of the + ** allocation. This is so as to ensure that the buffer is big enough + ** to hold the current doclist AND'd with any other doclist. If the + ** doclists are stored in order=ASC order, this padding would not be + ** required (since the size of [doclistA AND doclistB] is always less + ** than or equal to the size of [doclistA] in that case). But this is + ** not true for order=DESC. For example, a doclist containing (1, -1) + ** may be smaller than (-1), as in the first example the -1 may be stored + ** as a single-byte delta, whereas in the second it must be stored as a + ** FTS3_VARINT_MAX byte varint. + ** + ** Similar padding is added in the fts3DoclistOrMerge() function. + */ + pTS->aaOutput[0] = sqlite3_malloc(nDoclist + FTS3_VARINT_MAX + 1); pTS->anOutput[0] = nDoclist; if( pTS->aaOutput[0] ){ memcpy(pTS->aaOutput[0], aDoclist, nDoclist); @@ -134074,7 +135810,7 @@ static int fts3SegReaderCursor( ** calls out here. */ if( iLevel<0 && p->aIndex ){ Fts3SegReader *pSeg = 0; - rc = sqlite3Fts3SegReaderPending(p, iIndex, zTerm, nTerm, isPrefix, &pSeg); + rc = sqlite3Fts3SegReaderPending(p, iIndex, zTerm, nTerm, isPrefix||isScan, &pSeg); if( rc==SQLITE_OK && pSeg ){ rc = fts3SegReaderCursorAppend(pCsr, pSeg); } @@ -134477,10 +136213,17 @@ static int fts3FilterMethod( ** row by docid. */ if( eSearch==FTS3_FULLSCAN_SEARCH ){ - zSql = sqlite3_mprintf( - "SELECT %s ORDER BY rowid %s", - p->zReadExprlist, (pCsr->bDesc ? "DESC" : "ASC") - ); + if( pDocidGe || pDocidLe ){ + zSql = sqlite3_mprintf( + "SELECT %s WHERE rowid BETWEEN %lld AND %lld ORDER BY rowid %s", + p->zReadExprlist, pCsr->iMinDocid, pCsr->iMaxDocid, + (pCsr->bDesc ? "DESC" : "ASC") + ); + }else{ + zSql = sqlite3_mprintf("SELECT %s ORDER BY rowid %s", + p->zReadExprlist, (pCsr->bDesc ? "DESC" : "ASC") + ); + } if( zSql ){ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0); sqlite3_free(zSql); @@ -134716,11 +136459,31 @@ static void fts3ReversePoslist(char *pStart, char **ppPoslist){ char *p = &(*ppPoslist)[-2]; char c = 0; + /* Skip backwards passed any trailing 0x00 bytes added by NearTrim() */ while( p>pStart && (c=*p--)==0 ); + + /* Search backwards for a varint with value zero (the end of the previous + ** poslist). This is an 0x00 byte preceded by some byte that does not + ** have the 0x80 bit set. */ while( p>pStart && (*p & 0x80) | c ){ c = *p--; } - if( p>pStart ){ p = &p[2]; } + assert( p==pStart || c==0 ); + + /* At this point p points to that preceding byte without the 0x80 bit + ** set. So to find the start of the poslist, skip forward 2 bytes then + ** over a varint. + ** + ** Normally. The other case is that p==pStart and the poslist to return + ** is the first in the doclist. In this case do not skip forward 2 bytes. + ** The second part of the if condition (c==0 && *ppPoslist>&p[2]) + ** is required for cases where the first byte of a doclist and the + ** doclist is empty. For example, if the first docid is 10, a doclist + ** that begins with: + ** + ** 0x0A 0x00 + */ + if( p>pStart || (c==0 && *ppPoslist>&p[2]) ){ p = &p[2]; } while( *p++&0x80 ); *ppPoslist = p; } @@ -134791,6 +136554,8 @@ static void fts3SnippetFunc( } if( !zEllipsis || !zEnd || !zStart ){ sqlite3_result_error_nomem(pContext); + }else if( nToken==0 ){ + sqlite3_result_text(pContext, "", -1, SQLITE_STATIC); }else if( SQLITE_OK==fts3CursorSeek(pContext, pCsr) ){ sqlite3Fts3Snippet(pContext, pCsr, zStart, zEnd, zEllipsis, iCol, nToken); } @@ -135226,14 +136991,17 @@ static void fts3EvalAllocateReaders( ** This function assumes that pList points to a buffer allocated using ** sqlite3_malloc(). This function takes responsibility for eventually ** freeing the buffer. +** +** SQLITE_OK is returned if successful, or SQLITE_NOMEM if an error occurs. */ -static void fts3EvalPhraseMergeToken( +static int fts3EvalPhraseMergeToken( Fts3Table *pTab, /* FTS Table pointer */ Fts3Phrase *p, /* Phrase to merge pList/nList into */ int iToken, /* Token pList/nList corresponds to */ char *pList, /* Pointer to doclist */ int nList /* Number of bytes in pList */ ){ + int rc = SQLITE_OK; assert( iToken!=p->iDoclistToken ); if( pList==0 ){ @@ -135272,13 +137040,16 @@ static void fts3EvalPhraseMergeToken( nDiff = p->iDoclistToken - iToken; } - fts3DoclistPhraseMerge(pTab->bDescIdx, nDiff, pLeft, nLeft, pRight,&nRight); + rc = fts3DoclistPhraseMerge( + pTab->bDescIdx, nDiff, pLeft, nLeft, &pRight, &nRight + ); sqlite3_free(pLeft); p->doclist.aAll = pRight; p->doclist.nAll = nRight; } if( iToken>p->iDoclistToken ) p->iDoclistToken = iToken; + return rc; } /* @@ -135304,7 +137075,7 @@ static int fts3EvalPhraseLoad( char *pThis = 0; rc = fts3TermSelect(pTab, pToken, p->iColumn, &nThis, &pThis); if( rc==SQLITE_OK ){ - fts3EvalPhraseMergeToken(pTab, p, iToken, pThis, nThis); + rc = fts3EvalPhraseMergeToken(pTab, p, iToken, pThis, nThis); } } assert( pToken->pSegcsr==0 ); @@ -135846,12 +137617,14 @@ static void fts3EvalStartReaders( ){ if( pExpr && SQLITE_OK==*pRc ){ if( pExpr->eType==FTSQUERY_PHRASE ){ - int i; int nToken = pExpr->pPhrase->nToken; - for(i=0; ipPhrase->aToken[i].pDeferred==0 ) break; + if( nToken ){ + int i; + for(i=0; ipPhrase->aToken[i].pDeferred==0 ) break; + } + pExpr->bDeferred = (i==nToken); } - pExpr->bDeferred = (i==nToken); *pRc = fts3EvalPhraseStart(pCsr, 1, pExpr->pPhrase); }else{ fts3EvalStartReaders(pCsr, pExpr->pLeft, pRc); @@ -136106,9 +137879,13 @@ static int fts3EvalSelectDeferred( char *pList = 0; rc = fts3TermSelect(pTab, pToken, pTC->iCol, &nList, &pList); assert( rc==SQLITE_OK || pList==0 ); + if( rc==SQLITE_OK ){ + rc = fts3EvalPhraseMergeToken( + pTab, pTC->pPhrase, pTC->iToken,pList,nList + ); + } if( rc==SQLITE_OK ){ int nCount; - fts3EvalPhraseMergeToken(pTab, pTC->pPhrase, pTC->iToken,pList,nList); nCount = fts3DoclistCountDocids( pTC->pPhrase->doclist.aAll, pTC->pPhrase->doclist.nAll ); @@ -136333,6 +138110,22 @@ static void fts3EvalNextRow( } pExpr->iDocid = pLeft->iDocid; pExpr->bEof = (pLeft->bEof || pRight->bEof); + if( pExpr->eType==FTSQUERY_NEAR && pExpr->bEof ){ + if( pRight->pPhrase && pRight->pPhrase->doclist.aAll ){ + Fts3Doclist *pDl = &pRight->pPhrase->doclist; + while( *pRc==SQLITE_OK && pRight->bEof==0 ){ + memset(pDl->pList, 0, pDl->nList); + fts3EvalNextRow(pCsr, pRight, pRc); + } + } + if( pLeft->pPhrase && pLeft->pPhrase->doclist.aAll ){ + Fts3Doclist *pDl = &pLeft->pPhrase->doclist; + while( *pRc==SQLITE_OK && pLeft->bEof==0 ){ + memset(pDl->pList, 0, pDl->nList); + fts3EvalNextRow(pCsr, pLeft, pRc); + } + } + } } break; } @@ -136705,6 +138498,7 @@ static void fts3EvalRestart( } pPhrase->doclist.pNextDocid = 0; pPhrase->doclist.iDocid = 0; + pPhrase->pOrPoslist = 0; } pExpr->iDocid = 0; @@ -136950,8 +138744,8 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist( iDocid = pExpr->iDocid; pIter = pPhrase->doclist.pList; if( iDocid!=pCsr->iPrevId || pExpr->bEof ){ + int rc = SQLITE_OK; int bDescDoclist = pTab->bDescIdx; /* For DOCID_CMP macro */ - int iMul; /* +1 if csr dir matches index dir, else -1 */ int bOr = 0; u8 bEof = 0; u8 bTreeEof = 0; @@ -136975,72 +138769,44 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist( ** an incremental phrase. Load the entire doclist for the phrase ** into memory in this case. */ if( pPhrase->bIncr ){ - int rc = SQLITE_OK; - int bEofSave = pExpr->bEof; - fts3EvalRestart(pCsr, pExpr, &rc); - while( rc==SQLITE_OK && !pExpr->bEof ){ - fts3EvalNextRow(pCsr, pExpr, &rc); - if( bEofSave==0 && pExpr->iDocid==iDocid ) break; + int bEofSave = pNear->bEof; + fts3EvalRestart(pCsr, pNear, &rc); + while( rc==SQLITE_OK && !pNear->bEof ){ + fts3EvalNextRow(pCsr, pNear, &rc); + if( bEofSave==0 && pNear->iDocid==iDocid ) break; } - pIter = pPhrase->doclist.pList; assert( rc!=SQLITE_OK || pPhrase->bIncr==0 ); - if( rc!=SQLITE_OK ) return rc; } - - iMul = ((pCsr->bDesc==bDescDoclist) ? 1 : -1); - while( bTreeEof==1 - && pNear->bEof==0 - && (DOCID_CMP(pNear->iDocid, pCsr->iPrevId) * iMul)<0 - ){ - int rc = SQLITE_OK; - fts3EvalNextRow(pCsr, pExpr, &rc); - if( rc!=SQLITE_OK ) return rc; - iDocid = pExpr->iDocid; - pIter = pPhrase->doclist.pList; - } - - bEof = (pPhrase->doclist.nAll==0); - assert( bDescDoclist==0 || bDescDoclist==1 ); - assert( pCsr->bDesc==0 || pCsr->bDesc==1 ); - - if( bEof==0 ){ - if( pCsr->bDesc==bDescDoclist ){ - int dummy; - if( pNear->bEof ){ - /* This expression is already at EOF. So position it to point to the - ** last entry in the doclist at pPhrase->doclist.aAll[]. Variable - ** iDocid is already set for this entry, so all that is required is - ** to set pIter to point to the first byte of the last position-list - ** in the doclist. - ** - ** It would also be correct to set pIter and iDocid to zero. In - ** this case, the first call to sqltie3Fts4DoclistPrev() below - ** would also move the iterator to point to the last entry in the - ** doclist. However, this is expensive, as to do so it has to - ** iterate through the entire doclist from start to finish (since - ** it does not know the docid for the last entry). */ - pIter = &pPhrase->doclist.aAll[pPhrase->doclist.nAll-1]; - fts3ReversePoslist(pPhrase->doclist.aAll, &pIter); - } - while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)>0 ) && bEof==0 ){ - sqlite3Fts3DoclistPrev( - bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll, - &pIter, &iDocid, &dummy, &bEof - ); - } - }else{ - if( pNear->bEof ){ - pIter = 0; - iDocid = 0; - } - while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)<0 ) && bEof==0 ){ - sqlite3Fts3DoclistNext( - bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll, - &pIter, &iDocid, &bEof - ); - } + if( bTreeEof ){ + while( rc==SQLITE_OK && !pNear->bEof ){ + fts3EvalNextRow(pCsr, pNear, &rc); } } + if( rc!=SQLITE_OK ) return rc; + + pIter = pPhrase->pOrPoslist; + iDocid = pPhrase->iOrDocid; + if( pCsr->bDesc==bDescDoclist ){ + bEof = !pPhrase->doclist.nAll || + (pIter >= (pPhrase->doclist.aAll + pPhrase->doclist.nAll)); + while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)<0 ) && bEof==0 ){ + sqlite3Fts3DoclistNext( + bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll, + &pIter, &iDocid, &bEof + ); + } + }else{ + bEof = !pPhrase->doclist.nAll || (pIter && pIter<=pPhrase->doclist.aAll); + while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)>0 ) && bEof==0 ){ + int dummy; + sqlite3Fts3DoclistPrev( + bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll, + &pIter, &iDocid, &dummy, &bEof + ); + } + } + pPhrase->pOrPoslist = pIter; + pPhrase->iOrDocid = iDocid; if( bEof || iDocid!=pCsr->iPrevId ) pIter = 0; } @@ -137054,10 +138820,13 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist( } while( iThisxCreate(iArg, aArg, ppTok); assert( rc!=SQLITE_OK || *ppTok ); if( rc!=SQLITE_OK ){ - *pzErr = sqlite3_mprintf("unknown tokenizer"); + sqlite3Fts3ErrMsg(pzErr, "unknown tokenizer"); }else{ (*ppTok)->pModule = m; } @@ -140272,9 +142047,9 @@ static void testFunc( p = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash, zName, nName+1); if( !p ){ - char *zErr = sqlite3_mprintf("unknown tokenizer: %s", zName); - sqlite3_result_error(context, zErr, -1); - sqlite3_free(zErr); + char *zErr2 = sqlite3_mprintf("unknown tokenizer: %s", zName); + sqlite3_result_error(context, zErr2, -1); + sqlite3_free(zErr2); return; } @@ -140809,7 +142584,7 @@ static int fts3tokQueryTokenizer( p = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash, zName, nName+1); if( !p ){ - *pzErr = sqlite3_mprintf("unknown tokenizer: %s", zName); + sqlite3Fts3ErrMsg(pzErr, "unknown tokenizer: %s", zName); return SQLITE_ERROR; } @@ -141506,7 +143281,7 @@ static int fts3SqlStmt( /* 25 */ "", /* 26 */ "DELETE FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?", -/* 27 */ "SELECT DISTINCT level / (1024 * ?) FROM %Q.'%q_segdir'", +/* 27 */ "SELECT ? UNION SELECT level / (1024 * ?) FROM %Q.'%q_segdir'", /* This statement is used to determine which level to read the input from ** when performing an incremental merge. It returns the absolute level number @@ -142805,7 +144580,10 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderNew( ** an array of pending terms by term. This occurs as part of flushing ** the contents of the pending-terms hash table to the database. */ -static int fts3CompareElemByTerm(const void *lhs, const void *rhs){ +static int SQLITE_CDECL fts3CompareElemByTerm( + const void *lhs, + const void *rhs +){ char *z1 = fts3HashKey(*(Fts3HashElem **)lhs); char *z2 = fts3HashKey(*(Fts3HashElem **)rhs); int n1 = fts3HashKeysize(*(Fts3HashElem **)lhs); @@ -144621,7 +146399,8 @@ static int fts3DoOptimize(Fts3Table *p, int bReturnDone){ rc = fts3SqlStmt(p, SQL_SELECT_ALL_LANGID, &pAllLangid, 0); if( rc==SQLITE_OK ){ int rc2; - sqlite3_bind_int(pAllLangid, 1, p->nIndex); + sqlite3_bind_int(pAllLangid, 1, p->iPrevLangid); + sqlite3_bind_int(pAllLangid, 2, p->nIndex); while( sqlite3_step(pAllLangid)==SQLITE_ROW ){ int i; int iLangid = sqlite3_column_int(pAllLangid, 0); @@ -145953,7 +147732,7 @@ static int fts3IncrmergeHintPop(Blob *pHint, i64 *piAbsLevel, int *pnInput){ pHint->n = i; i += sqlite3Fts3GetVarint(&pHint->a[i], piAbsLevel); i += fts3GetVarint32(&pHint->a[i], pnInput); - if( i!=nHint ) return SQLITE_CORRUPT_VTAB; + if( i!=nHint ) return FTS_CORRUPT_VTAB; return SQLITE_OK; } @@ -146321,7 +148100,8 @@ static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){ rc = fts3SqlStmt(p, SQL_SELECT_ALL_LANGID, &pAllLangid, 0); if( rc==SQLITE_OK ){ int rc2; - sqlite3_bind_int(pAllLangid, 1, p->nIndex); + sqlite3_bind_int(pAllLangid, 1, p->iPrevLangid); + sqlite3_bind_int(pAllLangid, 2, p->nIndex); while( rc==SQLITE_OK && sqlite3_step(pAllLangid)==SQLITE_ROW ){ int iLangid = sqlite3_column_int(pAllLangid, 0); int i; @@ -146334,7 +148114,6 @@ static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){ } /* This block calculates the checksum according to the %_content table */ - rc = fts3SqlStmt(p, SQL_SELECT_ALL_LANGID, &pAllLangid, 0); if( rc==SQLITE_OK ){ sqlite3_tokenizer_module const *pModule = p->pTokenizer->pModule; sqlite3_stmt *pStmt = 0; @@ -146431,7 +148210,7 @@ static int fts3DoIntegrityCheck( int rc; int bOk = 0; rc = fts3IntegrityCheck(p, &bOk); - if( rc==SQLITE_OK && bOk==0 ) rc = SQLITE_CORRUPT_VTAB; + if( rc==SQLITE_OK && bOk==0 ) rc = FTS_CORRUPT_VTAB; return rc; } @@ -146869,6 +148648,7 @@ SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *p){ #define FTS3_MATCHINFO_LENGTH 'l' /* nCol values */ #define FTS3_MATCHINFO_LCS 's' /* nCol values */ #define FTS3_MATCHINFO_HITS 'x' /* 3*nCol*nPhrase values */ +#define FTS3_MATCHINFO_LHITS 'y' /* nCol*nPhrase values */ /* ** The default value for the second argument to matchinfo(). @@ -147284,37 +149064,39 @@ static int fts3BestSnippet( sIter.nSnippet = nSnippet; sIter.nPhrase = nList; sIter.iCurrent = -1; - (void)fts3ExprIterate(pCsr->pExpr, fts3SnippetFindPositions, (void *)&sIter); + rc = fts3ExprIterate(pCsr->pExpr, fts3SnippetFindPositions, (void *)&sIter); + if( rc==SQLITE_OK ){ - /* Set the *pmSeen output variable. */ - for(i=0; iiCol = iCol; - while( !fts3SnippetNextCandidate(&sIter) ){ - int iPos; - int iScore; - u64 mCover; - u64 mHighlight; - fts3SnippetDetails(&sIter, mCovered, &iPos, &iScore, &mCover, &mHighlight); - assert( iScore>=0 ); - if( iScore>iBestScore ){ - pFragment->iPos = iPos; - pFragment->hlmask = mHighlight; - pFragment->covered = mCover; - iBestScore = iScore; + /* Loop through all candidate snippets. Store the best snippet in + ** *pFragment. Store its associated 'score' in iBestScore. + */ + pFragment->iCol = iCol; + while( !fts3SnippetNextCandidate(&sIter) ){ + int iPos; + int iScore; + u64 mCover; + u64 mHighlite; + fts3SnippetDetails(&sIter, mCovered, &iPos, &iScore, &mCover,&mHighlite); + assert( iScore>=0 ); + if( iScore>iBestScore ){ + pFragment->iPos = iPos; + pFragment->hlmask = mHighlite; + pFragment->covered = mCover; + iBestScore = iScore; + } } - } + *piScore = iBestScore; + } sqlite3_free(sIter.aPhrase); - *piScore = iBestScore; - return SQLITE_OK; + return rc; } @@ -147522,8 +149304,12 @@ static int fts3SnippetText( ** required. They are required if (a) this is not the first fragment, ** or (b) this fragment does not begin at position 0 of its column. */ - if( rc==SQLITE_OK && (iPos>0 || iFragment>0) ){ - rc = fts3StringAppend(pOut, zEllipsis, -1); + if( rc==SQLITE_OK ){ + if( iPos>0 || iFragment>0 ){ + rc = fts3StringAppend(pOut, zEllipsis, -1); + }else if( iBegin ){ + rc = fts3StringAppend(pOut, zDoc, iBegin); + } } if( rc!=SQLITE_OK || iCurrentpCursor->base.pVtab; + int rc = SQLITE_OK; + int iStart = iPhrase * p->nCol; + Fts3Expr *pEof; /* Ancestor node already at EOF */ + + /* This must be a phrase */ + assert( pExpr->pPhrase ); + + /* Initialize all output integers to zero. */ + memset(&p->aMatchinfo[iStart], 0, sizeof(u32) * p->nCol); + + /* Check if this or any parent node is at EOF. If so, then all output + ** values are zero. */ + for(pEof=pExpr; pEof && pEof->bEof==0; pEof=pEof->pParent); + + if( pEof==0 && pExpr->iDocid==p->pCursor->iPrevId ){ + Fts3Phrase *pPhrase = pExpr->pPhrase; + char *pIter = pPhrase->doclist.pList; + int iCol = 0; + + while( 1 ){ + int nHit = fts3ColumnlistCount(&pIter); + if( (pPhrase->iColumn>=pTab->nColumn || pPhrase->iColumn==iCol) ){ + p->aMatchinfo[iStart + iCol] = (u32)nHit; + } + assert( *pIter==0x00 || *pIter==0x01 ); + if( *pIter!=0x01 ) break; + pIter++; + pIter += fts3GetVarint32(pIter, &iCol); + } + } + + return rc; +} + static int fts3MatchinfoCheck( Fts3Table *pTab, char cArg, @@ -147657,10 +149488,11 @@ static int fts3MatchinfoCheck( || (cArg==FTS3_MATCHINFO_LENGTH && pTab->bHasDocsize) || (cArg==FTS3_MATCHINFO_LCS) || (cArg==FTS3_MATCHINFO_HITS) + || (cArg==FTS3_MATCHINFO_LHITS) ){ return SQLITE_OK; } - *pzErr = sqlite3_mprintf("unrecognized matchinfo request: %c", cArg); + sqlite3Fts3ErrMsg(pzErr, "unrecognized matchinfo request: %c", cArg); return SQLITE_ERROR; } @@ -147680,6 +149512,10 @@ static int fts3MatchinfoSize(MatchInfo *pInfo, char cArg){ nVal = pInfo->nCol; break; + case FTS3_MATCHINFO_LHITS: + nVal = pInfo->nCol * pInfo->nPhrase; + break; + default: assert( cArg==FTS3_MATCHINFO_HITS ); nVal = pInfo->nCol * pInfo->nPhrase * 3; @@ -147934,6 +149770,10 @@ static int fts3MatchinfoValues( } break; + case FTS3_MATCHINFO_LHITS: + (void)fts3ExprIterate(pCsr->pExpr, fts3ExprLHitsCb, (void*)pInfo); + break; + default: { Fts3Expr *pExpr; assert( zArg[i]==FTS3_MATCHINFO_HITS ); @@ -148089,7 +149929,7 @@ SQLITE_PRIVATE void sqlite3Fts3Snippet( */ for(iRead=0; iReadnColumn; iRead++){ SnippetFragment sF = {0, 0, 0, 0}; - int iS; + int iS = 0; if( iCol>=0 && iRead!=iCol ) continue; /* Find the best snippet of nFToken tokens in column iRead. */ @@ -151946,11 +153786,19 @@ static int rtreeUpdate( if( nData>1 ){ int ii; - /* Populate the cell.aCoord[] array. The first coordinate is azData[3]. */ - assert( nData==(pRtree->nDim*2 + 3) ); + /* Populate the cell.aCoord[] array. The first coordinate is azData[3]. + ** + ** NB: nData can only be less than nDim*2+3 if the rtree is mis-declared + ** with "column" that are interpreted as table constraints. + ** Example: CREATE VIRTUAL TABLE bad USING rtree(x,y,CHECK(y>5)); + ** This problem was discovered after years of use, so we silently ignore + ** these kinds of misdeclared tables to avoid breaking any legacy. + */ + assert( nData<=(pRtree->nDim*2 + 3) ); + #ifndef SQLITE_RTREE_INT_ONLY if( pRtree->eCoordType==RTREE_COORD_REAL32 ){ - for(ii=0; ii<(pRtree->nDim*2); ii+=2){ + for(ii=0; iicell.aCoord[ii+1].f ){ @@ -151961,7 +153809,7 @@ static int rtreeUpdate( }else #endif { - for(ii=0; ii<(pRtree->nDim*2); ii+=2){ + for(ii=0; iicell.aCoord[ii+1].i ){ @@ -152532,7 +154380,7 @@ static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){ /* ** Register a new geometry function for use with the r-tree MATCH operator. */ -SQLITE_API int sqlite3_rtree_geometry_callback( +SQLITE_API int SQLITE_STDCALL sqlite3_rtree_geometry_callback( sqlite3 *db, /* Register SQL function on this connection */ const char *zGeom, /* Name of the new SQL function */ int (*xGeom)(sqlite3_rtree_geometry*,int,RtreeDValue*,int*), /* Callback */ @@ -152556,7 +154404,7 @@ SQLITE_API int sqlite3_rtree_geometry_callback( ** Register a new 2nd-generation geometry function for use with the ** r-tree MATCH operator. */ -SQLITE_API int sqlite3_rtree_query_callback( +SQLITE_API int SQLITE_STDCALL sqlite3_rtree_query_callback( sqlite3 *db, /* Register SQL function on this connection */ const char *zQueryFunc, /* Name of new SQL function */ int (*xQueryFunc)(sqlite3_rtree_query_info*), /* Callback */ @@ -152581,7 +154429,7 @@ SQLITE_API int sqlite3_rtree_query_callback( #ifdef _WIN32 __declspec(dllexport) #endif -SQLITE_API int sqlite3_rtree_init( +SQLITE_API int SQLITE_STDCALL sqlite3_rtree_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi @@ -153086,7 +154934,7 @@ SQLITE_PRIVATE int sqlite3IcuInit(sqlite3 *db){ #ifdef _WIN32 __declspec(dllexport) #endif -SQLITE_API int sqlite3_icu_init( +SQLITE_API int SQLITE_STDCALL sqlite3_icu_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi @@ -153361,3 +155209,631 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule( #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ /************** End of fts3_icu.c ********************************************/ +/************** Begin file dbstat.c ******************************************/ +/* +** 2010 July 12 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This file contains an implementation of the "dbstat" virtual table. +** +** The dbstat virtual table is used to extract low-level formatting +** information from an SQLite database in order to implement the +** "sqlite3_analyzer" utility. See the ../tool/spaceanal.tcl script +** for an example implementation. +*/ + +#if (defined(SQLITE_ENABLE_DBSTAT_VTAB) || defined(SQLITE_TEST)) \ + && !defined(SQLITE_OMIT_VIRTUALTABLE) + +/* +** Page paths: +** +** The value of the 'path' column describes the path taken from the +** root-node of the b-tree structure to each page. The value of the +** root-node path is '/'. +** +** The value of the path for the left-most child page of the root of +** a b-tree is '/000/'. (Btrees store content ordered from left to right +** so the pages to the left have smaller keys than the pages to the right.) +** The next to left-most child of the root page is +** '/001', and so on, each sibling page identified by a 3-digit hex +** value. The children of the 451st left-most sibling have paths such +** as '/1c2/000/, '/1c2/001/' etc. +** +** Overflow pages are specified by appending a '+' character and a +** six-digit hexadecimal value to the path to the cell they are linked +** from. For example, the three overflow pages in a chain linked from +** the left-most cell of the 450th child of the root page are identified +** by the paths: +** +** '/1c2/000+000000' // First page in overflow chain +** '/1c2/000+000001' // Second page in overflow chain +** '/1c2/000+000002' // Third page in overflow chain +** +** If the paths are sorted using the BINARY collation sequence, then +** the overflow pages associated with a cell will appear earlier in the +** sort-order than its child page: +** +** '/1c2/000/' // Left-most child of 451st child of root +*/ +#define VTAB_SCHEMA \ + "CREATE TABLE xx( " \ + " name STRING, /* Name of table or index */" \ + " path INTEGER, /* Path to page from root */" \ + " pageno INTEGER, /* Page number */" \ + " pagetype STRING, /* 'internal', 'leaf' or 'overflow' */" \ + " ncell INTEGER, /* Cells on page (0 for overflow) */" \ + " payload INTEGER, /* Bytes of payload on this page */" \ + " unused INTEGER, /* Bytes of unused space on this page */" \ + " mx_payload INTEGER, /* Largest payload size of all cells */" \ + " pgoffset INTEGER, /* Offset of page in file */" \ + " pgsize INTEGER /* Size of the page */" \ + ");" + + +typedef struct StatTable StatTable; +typedef struct StatCursor StatCursor; +typedef struct StatPage StatPage; +typedef struct StatCell StatCell; + +struct StatCell { + int nLocal; /* Bytes of local payload */ + u32 iChildPg; /* Child node (or 0 if this is a leaf) */ + int nOvfl; /* Entries in aOvfl[] */ + u32 *aOvfl; /* Array of overflow page numbers */ + int nLastOvfl; /* Bytes of payload on final overflow page */ + int iOvfl; /* Iterates through aOvfl[] */ +}; + +struct StatPage { + u32 iPgno; + DbPage *pPg; + int iCell; + + char *zPath; /* Path to this page */ + + /* Variables populated by statDecodePage(): */ + u8 flags; /* Copy of flags byte */ + int nCell; /* Number of cells on page */ + int nUnused; /* Number of unused bytes on page */ + StatCell *aCell; /* Array of parsed cells */ + u32 iRightChildPg; /* Right-child page number (or 0) */ + int nMxPayload; /* Largest payload of any cell on this page */ +}; + +struct StatCursor { + sqlite3_vtab_cursor base; + sqlite3_stmt *pStmt; /* Iterates through set of root pages */ + int isEof; /* After pStmt has returned SQLITE_DONE */ + + StatPage aPage[32]; + int iPage; /* Current entry in aPage[] */ + + /* Values to return. */ + char *zName; /* Value of 'name' column */ + char *zPath; /* Value of 'path' column */ + u32 iPageno; /* Value of 'pageno' column */ + char *zPagetype; /* Value of 'pagetype' column */ + int nCell; /* Value of 'ncell' column */ + int nPayload; /* Value of 'payload' column */ + int nUnused; /* Value of 'unused' column */ + int nMxPayload; /* Value of 'mx_payload' column */ + i64 iOffset; /* Value of 'pgOffset' column */ + int szPage; /* Value of 'pgSize' column */ +}; + +struct StatTable { + sqlite3_vtab base; + sqlite3 *db; +}; + +#ifndef get2byte +# define get2byte(x) ((x)[0]<<8 | (x)[1]) +#endif + +/* +** Connect to or create a statvfs virtual table. +*/ +static int statConnect( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + StatTable *pTab = 0; + int rc = SQLITE_OK; + + rc = sqlite3_declare_vtab(db, VTAB_SCHEMA); + if( rc==SQLITE_OK ){ + pTab = (StatTable *)sqlite3_malloc64(sizeof(StatTable)); + if( pTab==0 ) rc = SQLITE_NOMEM; + } + + assert( rc==SQLITE_OK || pTab==0 ); + if( rc==SQLITE_OK ){ + memset(pTab, 0, sizeof(StatTable)); + pTab->db = db; + } + + *ppVtab = (sqlite3_vtab*)pTab; + return rc; +} + +/* +** Disconnect from or destroy a statvfs virtual table. +*/ +static int statDisconnect(sqlite3_vtab *pVtab){ + sqlite3_free(pVtab); + return SQLITE_OK; +} + +/* +** There is no "best-index". This virtual table always does a linear +** scan of the binary VFS log file. +*/ +static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + + /* Records are always returned in ascending order of (name, path). + ** If this will satisfy the client, set the orderByConsumed flag so that + ** SQLite does not do an external sort. + */ + if( ( pIdxInfo->nOrderBy==1 + && pIdxInfo->aOrderBy[0].iColumn==0 + && pIdxInfo->aOrderBy[0].desc==0 + ) || + ( pIdxInfo->nOrderBy==2 + && pIdxInfo->aOrderBy[0].iColumn==0 + && pIdxInfo->aOrderBy[0].desc==0 + && pIdxInfo->aOrderBy[1].iColumn==1 + && pIdxInfo->aOrderBy[1].desc==0 + ) + ){ + pIdxInfo->orderByConsumed = 1; + } + + pIdxInfo->estimatedCost = 10.0; + return SQLITE_OK; +} + +/* +** Open a new statvfs cursor. +*/ +static int statOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ + StatTable *pTab = (StatTable *)pVTab; + StatCursor *pCsr; + int rc; + + pCsr = (StatCursor *)sqlite3_malloc64(sizeof(StatCursor)); + if( pCsr==0 ){ + rc = SQLITE_NOMEM; + }else{ + memset(pCsr, 0, sizeof(StatCursor)); + pCsr->base.pVtab = pVTab; + + rc = sqlite3_prepare_v2(pTab->db, + "SELECT 'sqlite_master' AS name, 1 AS rootpage, 'table' AS type" + " UNION ALL " + "SELECT name, rootpage, type FROM sqlite_master WHERE rootpage!=0" + " ORDER BY name", -1, + &pCsr->pStmt, 0 + ); + if( rc!=SQLITE_OK ){ + sqlite3_free(pCsr); + pCsr = 0; + } + } + + *ppCursor = (sqlite3_vtab_cursor *)pCsr; + return rc; +} + +static void statClearPage(StatPage *p){ + int i; + if( p->aCell ){ + for(i=0; inCell; i++){ + sqlite3_free(p->aCell[i].aOvfl); + } + sqlite3_free(p->aCell); + } + sqlite3PagerUnref(p->pPg); + sqlite3_free(p->zPath); + memset(p, 0, sizeof(StatPage)); +} + +static void statResetCsr(StatCursor *pCsr){ + int i; + sqlite3_reset(pCsr->pStmt); + for(i=0; iaPage); i++){ + statClearPage(&pCsr->aPage[i]); + } + pCsr->iPage = 0; + sqlite3_free(pCsr->zPath); + pCsr->zPath = 0; +} + +/* +** Close a statvfs cursor. +*/ +static int statClose(sqlite3_vtab_cursor *pCursor){ + StatCursor *pCsr = (StatCursor *)pCursor; + statResetCsr(pCsr); + sqlite3_finalize(pCsr->pStmt); + sqlite3_free(pCsr); + return SQLITE_OK; +} + +static void getLocalPayload( + int nUsable, /* Usable bytes per page */ + u8 flags, /* Page flags */ + int nTotal, /* Total record (payload) size */ + int *pnLocal /* OUT: Bytes stored locally */ +){ + int nLocal; + int nMinLocal; + int nMaxLocal; + + if( flags==0x0D ){ /* Table leaf node */ + nMinLocal = (nUsable - 12) * 32 / 255 - 23; + nMaxLocal = nUsable - 35; + }else{ /* Index interior and leaf nodes */ + nMinLocal = (nUsable - 12) * 32 / 255 - 23; + nMaxLocal = (nUsable - 12) * 64 / 255 - 23; + } + + nLocal = nMinLocal + (nTotal - nMinLocal) % (nUsable - 4); + if( nLocal>nMaxLocal ) nLocal = nMinLocal; + *pnLocal = nLocal; +} + +static int statDecodePage(Btree *pBt, StatPage *p){ + int nUnused; + int iOff; + int nHdr; + int isLeaf; + int szPage; + + u8 *aData = sqlite3PagerGetData(p->pPg); + u8 *aHdr = &aData[p->iPgno==1 ? 100 : 0]; + + p->flags = aHdr[0]; + p->nCell = get2byte(&aHdr[3]); + p->nMxPayload = 0; + + isLeaf = (p->flags==0x0A || p->flags==0x0D); + nHdr = 12 - isLeaf*4 + (p->iPgno==1)*100; + + nUnused = get2byte(&aHdr[5]) - nHdr - 2*p->nCell; + nUnused += (int)aHdr[7]; + iOff = get2byte(&aHdr[1]); + while( iOff ){ + nUnused += get2byte(&aData[iOff+2]); + iOff = get2byte(&aData[iOff]); + } + p->nUnused = nUnused; + p->iRightChildPg = isLeaf ? 0 : sqlite3Get4byte(&aHdr[8]); + szPage = sqlite3BtreeGetPageSize(pBt); + + if( p->nCell ){ + int i; /* Used to iterate through cells */ + int nUsable; /* Usable bytes per page */ + + sqlite3BtreeEnter(pBt); + nUsable = szPage - sqlite3BtreeGetReserveNoMutex(pBt); + sqlite3BtreeLeave(pBt); + p->aCell = sqlite3_malloc64((p->nCell+1) * sizeof(StatCell)); + if( p->aCell==0 ) return SQLITE_NOMEM; + memset(p->aCell, 0, (p->nCell+1) * sizeof(StatCell)); + + for(i=0; inCell; i++){ + StatCell *pCell = &p->aCell[i]; + + iOff = get2byte(&aData[nHdr+i*2]); + if( !isLeaf ){ + pCell->iChildPg = sqlite3Get4byte(&aData[iOff]); + iOff += 4; + } + if( p->flags==0x05 ){ + /* A table interior node. nPayload==0. */ + }else{ + u32 nPayload; /* Bytes of payload total (local+overflow) */ + int nLocal; /* Bytes of payload stored locally */ + iOff += getVarint32(&aData[iOff], nPayload); + if( p->flags==0x0D ){ + u64 dummy; + iOff += sqlite3GetVarint(&aData[iOff], &dummy); + } + if( nPayload>(u32)p->nMxPayload ) p->nMxPayload = nPayload; + getLocalPayload(nUsable, p->flags, nPayload, &nLocal); + pCell->nLocal = nLocal; + assert( nLocal>=0 ); + assert( nPayload>=(u32)nLocal ); + assert( nLocal<=(nUsable-35) ); + if( nPayload>(u32)nLocal ){ + int j; + int nOvfl = ((nPayload - nLocal) + nUsable-4 - 1) / (nUsable - 4); + pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4); + pCell->nOvfl = nOvfl; + pCell->aOvfl = sqlite3_malloc64(sizeof(u32)*nOvfl); + if( pCell->aOvfl==0 ) return SQLITE_NOMEM; + pCell->aOvfl[0] = sqlite3Get4byte(&aData[iOff+nLocal]); + for(j=1; jaOvfl[j-1]; + DbPage *pPg = 0; + rc = sqlite3PagerGet(sqlite3BtreePager(pBt), iPrev, &pPg); + if( rc!=SQLITE_OK ){ + assert( pPg==0 ); + return rc; + } + pCell->aOvfl[j] = sqlite3Get4byte(sqlite3PagerGetData(pPg)); + sqlite3PagerUnref(pPg); + } + } + } + } + } + + return SQLITE_OK; +} + +/* +** Populate the pCsr->iOffset and pCsr->szPage member variables. Based on +** the current value of pCsr->iPageno. +*/ +static void statSizeAndOffset(StatCursor *pCsr){ + StatTable *pTab = (StatTable *)((sqlite3_vtab_cursor *)pCsr)->pVtab; + Btree *pBt = pTab->db->aDb[0].pBt; + Pager *pPager = sqlite3BtreePager(pBt); + sqlite3_file *fd; + sqlite3_int64 x[2]; + + /* The default page size and offset */ + pCsr->szPage = sqlite3BtreeGetPageSize(pBt); + pCsr->iOffset = (i64)pCsr->szPage * (pCsr->iPageno - 1); + + /* If connected to a ZIPVFS backend, override the page size and + ** offset with actual values obtained from ZIPVFS. + */ + fd = sqlite3PagerFile(pPager); + x[0] = pCsr->iPageno; + if( sqlite3OsFileControl(fd, 230440, &x)==SQLITE_OK ){ + pCsr->iOffset = x[0]; + pCsr->szPage = (int)x[1]; + } +} + +/* +** Move a statvfs cursor to the next entry in the file. +*/ +static int statNext(sqlite3_vtab_cursor *pCursor){ + int rc; + int nPayload; + StatCursor *pCsr = (StatCursor *)pCursor; + StatTable *pTab = (StatTable *)pCursor->pVtab; + Btree *pBt = pTab->db->aDb[0].pBt; + Pager *pPager = sqlite3BtreePager(pBt); + + sqlite3_free(pCsr->zPath); + pCsr->zPath = 0; + +statNextRestart: + if( pCsr->aPage[0].pPg==0 ){ + rc = sqlite3_step(pCsr->pStmt); + if( rc==SQLITE_ROW ){ + int nPage; + u32 iRoot = (u32)sqlite3_column_int64(pCsr->pStmt, 1); + sqlite3PagerPagecount(pPager, &nPage); + if( nPage==0 ){ + pCsr->isEof = 1; + return sqlite3_reset(pCsr->pStmt); + } + rc = sqlite3PagerGet(pPager, iRoot, &pCsr->aPage[0].pPg); + pCsr->aPage[0].iPgno = iRoot; + pCsr->aPage[0].iCell = 0; + pCsr->aPage[0].zPath = sqlite3_mprintf("/"); + pCsr->iPage = 0; + }else{ + pCsr->isEof = 1; + return sqlite3_reset(pCsr->pStmt); + } + }else{ + + /* Page p itself has already been visited. */ + StatPage *p = &pCsr->aPage[pCsr->iPage]; + + while( p->iCellnCell ){ + StatCell *pCell = &p->aCell[p->iCell]; + if( pCell->iOvflnOvfl ){ + int nUsable; + sqlite3BtreeEnter(pBt); + nUsable = sqlite3BtreeGetPageSize(pBt) - + sqlite3BtreeGetReserveNoMutex(pBt); + sqlite3BtreeLeave(pBt); + pCsr->zName = (char *)sqlite3_column_text(pCsr->pStmt, 0); + pCsr->iPageno = pCell->aOvfl[pCell->iOvfl]; + pCsr->zPagetype = "overflow"; + pCsr->nCell = 0; + pCsr->nMxPayload = 0; + pCsr->zPath = sqlite3_mprintf( + "%s%.3x+%.6x", p->zPath, p->iCell, pCell->iOvfl + ); + if( pCell->iOvflnOvfl-1 ){ + pCsr->nUnused = 0; + pCsr->nPayload = nUsable - 4; + }else{ + pCsr->nPayload = pCell->nLastOvfl; + pCsr->nUnused = nUsable - 4 - pCsr->nPayload; + } + pCell->iOvfl++; + statSizeAndOffset(pCsr); + return SQLITE_OK; + } + if( p->iRightChildPg ) break; + p->iCell++; + } + + if( !p->iRightChildPg || p->iCell>p->nCell ){ + statClearPage(p); + if( pCsr->iPage==0 ) return statNext(pCursor); + pCsr->iPage--; + goto statNextRestart; /* Tail recursion */ + } + pCsr->iPage++; + assert( p==&pCsr->aPage[pCsr->iPage-1] ); + + if( p->iCell==p->nCell ){ + p[1].iPgno = p->iRightChildPg; + }else{ + p[1].iPgno = p->aCell[p->iCell].iChildPg; + } + rc = sqlite3PagerGet(pPager, p[1].iPgno, &p[1].pPg); + p[1].iCell = 0; + p[1].zPath = sqlite3_mprintf("%s%.3x/", p->zPath, p->iCell); + p->iCell++; + } + + + /* Populate the StatCursor fields with the values to be returned + ** by the xColumn() and xRowid() methods. + */ + if( rc==SQLITE_OK ){ + int i; + StatPage *p = &pCsr->aPage[pCsr->iPage]; + pCsr->zName = (char *)sqlite3_column_text(pCsr->pStmt, 0); + pCsr->iPageno = p->iPgno; + + rc = statDecodePage(pBt, p); + if( rc==SQLITE_OK ){ + statSizeAndOffset(pCsr); + + switch( p->flags ){ + case 0x05: /* table internal */ + case 0x02: /* index internal */ + pCsr->zPagetype = "internal"; + break; + case 0x0D: /* table leaf */ + case 0x0A: /* index leaf */ + pCsr->zPagetype = "leaf"; + break; + default: + pCsr->zPagetype = "corrupted"; + break; + } + pCsr->nCell = p->nCell; + pCsr->nUnused = p->nUnused; + pCsr->nMxPayload = p->nMxPayload; + pCsr->zPath = sqlite3_mprintf("%s", p->zPath); + nPayload = 0; + for(i=0; inCell; i++){ + nPayload += p->aCell[i].nLocal; + } + pCsr->nPayload = nPayload; + } + } + + return rc; +} + +static int statEof(sqlite3_vtab_cursor *pCursor){ + StatCursor *pCsr = (StatCursor *)pCursor; + return pCsr->isEof; +} + +static int statFilter( + sqlite3_vtab_cursor *pCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + StatCursor *pCsr = (StatCursor *)pCursor; + + statResetCsr(pCsr); + return statNext(pCursor); +} + +static int statColumn( + sqlite3_vtab_cursor *pCursor, + sqlite3_context *ctx, + int i +){ + StatCursor *pCsr = (StatCursor *)pCursor; + switch( i ){ + case 0: /* name */ + sqlite3_result_text(ctx, pCsr->zName, -1, SQLITE_STATIC); + break; + case 1: /* path */ + sqlite3_result_text(ctx, pCsr->zPath, -1, SQLITE_TRANSIENT); + break; + case 2: /* pageno */ + sqlite3_result_int64(ctx, pCsr->iPageno); + break; + case 3: /* pagetype */ + sqlite3_result_text(ctx, pCsr->zPagetype, -1, SQLITE_STATIC); + break; + case 4: /* ncell */ + sqlite3_result_int(ctx, pCsr->nCell); + break; + case 5: /* payload */ + sqlite3_result_int(ctx, pCsr->nPayload); + break; + case 6: /* unused */ + sqlite3_result_int(ctx, pCsr->nUnused); + break; + case 7: /* mx_payload */ + sqlite3_result_int(ctx, pCsr->nMxPayload); + break; + case 8: /* pgoffset */ + sqlite3_result_int64(ctx, pCsr->iOffset); + break; + case 9: /* pgsize */ + sqlite3_result_int(ctx, pCsr->szPage); + break; + } + return SQLITE_OK; +} + +static int statRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ + StatCursor *pCsr = (StatCursor *)pCursor; + *pRowid = pCsr->iPageno; + return SQLITE_OK; +} + +/* +** Invoke this routine to register the "dbstat" virtual table module +*/ +SQLITE_API int SQLITE_STDCALL sqlite3_dbstat_register(sqlite3 *db){ + static sqlite3_module dbstat_module = { + 0, /* iVersion */ + statConnect, /* xCreate */ + statConnect, /* xConnect */ + statBestIndex, /* xBestIndex */ + statDisconnect, /* xDisconnect */ + statDisconnect, /* xDestroy */ + statOpen, /* xOpen - open a cursor */ + statClose, /* xClose - close a cursor */ + statFilter, /* xFilter - configure scan constraints */ + statNext, /* xNext - advance a cursor */ + statEof, /* xEof - check for end of scan */ + statColumn, /* xColumn - read data */ + statRowid, /* xRowid - read data */ + 0, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindMethod */ + 0, /* xRename */ + }; + return sqlite3_create_module(db, "dbstat", &dbstat_module, 0); +} +#endif /* SQLITE_ENABLE_DBSTAT_VTAB */ + +/************** End of dbstat.c **********************************************/ diff --git a/TMessagesProj/jni/sqlite/sqlite3.h b/TMessagesProj/jni/sqlite/sqlite3.h index 07406477d..edb9e9c4b 100644 --- a/TMessagesProj/jni/sqlite/sqlite3.h +++ b/TMessagesProj/jni/sqlite/sqlite3.h @@ -43,16 +43,20 @@ extern "C" { /* -** Add the ability to override 'extern' +** Provide the ability to override linkage features of the interface. */ #ifndef SQLITE_EXTERN # define SQLITE_EXTERN extern #endif - #ifndef SQLITE_API # define SQLITE_API #endif - +#ifndef SQLITE_CDECL +# define SQLITE_CDECL +#endif +#ifndef SQLITE_STDCALL +# define SQLITE_STDCALL +#endif /* ** These no-op macros are used in front of interfaces to mark those @@ -107,9 +111,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.8.8.1" -#define SQLITE_VERSION_NUMBER 3008008 -#define SQLITE_SOURCE_ID "2015-01-20 16:51:25 f73337e3e289915a76ca96e7a05a1a8d4e890d55" +#define SQLITE_VERSION "3.8.10" +#define SQLITE_VERSION_NUMBER 3008010 +#define SQLITE_SOURCE_ID "2015-05-07 11:53:08 cf975957b9ae671f34bb65f049acf351e650d437" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -142,9 +146,9 @@ extern "C" { ** See also: [sqlite_version()] and [sqlite_source_id()]. */ SQLITE_API SQLITE_EXTERN const char sqlite3_version[]; -SQLITE_API const char *sqlite3_libversion(void); -SQLITE_API const char *sqlite3_sourceid(void); -SQLITE_API int sqlite3_libversion_number(void); +SQLITE_API const char *SQLITE_STDCALL sqlite3_libversion(void); +SQLITE_API const char *SQLITE_STDCALL sqlite3_sourceid(void); +SQLITE_API int SQLITE_STDCALL sqlite3_libversion_number(void); /* ** CAPI3REF: Run-Time Library Compilation Options Diagnostics @@ -169,8 +173,8 @@ SQLITE_API int sqlite3_libversion_number(void); ** [sqlite_compileoption_get()] and the [compile_options pragma]. */ #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS -SQLITE_API int sqlite3_compileoption_used(const char *zOptName); -SQLITE_API const char *sqlite3_compileoption_get(int N); +SQLITE_API int SQLITE_STDCALL sqlite3_compileoption_used(const char *zOptName); +SQLITE_API const char *SQLITE_STDCALL sqlite3_compileoption_get(int N); #endif /* @@ -209,7 +213,7 @@ SQLITE_API const char *sqlite3_compileoption_get(int N); ** ** See the [threading mode] documentation for additional information. */ -SQLITE_API int sqlite3_threadsafe(void); +SQLITE_API int SQLITE_STDCALL sqlite3_threadsafe(void); /* ** CAPI3REF: Database Connection Handle @@ -266,6 +270,7 @@ typedef sqlite_uint64 sqlite3_uint64; /* ** CAPI3REF: Closing A Database Connection +** DESTRUCTOR: sqlite3 ** ** ^The sqlite3_close() and sqlite3_close_v2() routines are destructors ** for the [sqlite3] object. @@ -305,8 +310,8 @@ typedef sqlite_uint64 sqlite3_uint64; ** ^Calling sqlite3_close() or sqlite3_close_v2() with a NULL pointer ** argument is a harmless no-op. */ -SQLITE_API int sqlite3_close(sqlite3*); -SQLITE_API int sqlite3_close_v2(sqlite3*); +SQLITE_API int SQLITE_STDCALL sqlite3_close(sqlite3*); +SQLITE_API int SQLITE_STDCALL sqlite3_close_v2(sqlite3*); /* ** The type for a callback function. @@ -317,6 +322,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**); /* ** CAPI3REF: One-Step Query Execution Interface +** METHOD: sqlite3 ** ** The sqlite3_exec() interface is a convenience wrapper around ** [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()], @@ -376,7 +382,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**); ** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running. ** */ -SQLITE_API int sqlite3_exec( +SQLITE_API int SQLITE_STDCALL sqlite3_exec( sqlite3*, /* An open database */ const char *sql, /* SQL to be evaluated */ int (*callback)(void*,int,char**,char**), /* Callback function */ @@ -756,14 +762,16 @@ struct sqlite3_io_methods { ** of the [sqlite3_io_methods] object and for the [sqlite3_file_control()] ** interface. ** +**
    +**
  • [[SQLITE_FCNTL_LOCKSTATE]] ** The [SQLITE_FCNTL_LOCKSTATE] opcode is used for debugging. This ** opcode causes the xFileControl method to write the current state of ** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED], ** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE]) ** into an integer that the pArg argument points to. This capability -** is used during testing and only needs to be supported when SQLITE_TEST -** is defined. -**
      +** is used during testing and is only available when the SQLITE_TEST +** compile-time option is used. +** **
    • [[SQLITE_FCNTL_SIZE_HINT]] ** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS ** layer a hint of how large the database file will grow to be during the @@ -888,7 +896,9 @@ struct sqlite3_io_methods { ** [PRAGMA] processing continues. ^If the [SQLITE_FCNTL_PRAGMA] ** file control returns [SQLITE_OK], then the parser assumes that the ** VFS has handled the PRAGMA itself and the parser generates a no-op -** prepared statement. ^If the [SQLITE_FCNTL_PRAGMA] file control returns +** prepared statement if result string is NULL, or that returns a copy +** of the result string if the string is non-NULL. +** ^If the [SQLITE_FCNTL_PRAGMA] file control returns ** any result code other than [SQLITE_OK] or [SQLITE_NOTFOUND], that means ** that the VFS encountered an error while handling the [PRAGMA] and the ** compilation of the PRAGMA fails with an error. ^The [SQLITE_FCNTL_PRAGMA] @@ -946,12 +956,19 @@ struct sqlite3_io_methods { ** pointed to by the pArg argument. This capability is used during testing ** and only needs to be supported when SQLITE_TEST is defined. ** +**
    • [[SQLITE_FCNTL_WAL_BLOCK]] +** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might +** be advantageous to block on the next WAL lock if the lock is not immediately +** available. The WAL subsystem issues this signal during rare +** circumstances in order to fix a problem with priority inversion. +** Applications should not use this file-control. +** **
    */ #define SQLITE_FCNTL_LOCKSTATE 1 -#define SQLITE_GET_LOCKPROXYFILE 2 -#define SQLITE_SET_LOCKPROXYFILE 3 -#define SQLITE_LAST_ERRNO 4 +#define SQLITE_FCNTL_GET_LOCKPROXYFILE 2 +#define SQLITE_FCNTL_SET_LOCKPROXYFILE 3 +#define SQLITE_FCNTL_LAST_ERRNO 4 #define SQLITE_FCNTL_SIZE_HINT 5 #define SQLITE_FCNTL_CHUNK_SIZE 6 #define SQLITE_FCNTL_FILE_POINTER 7 @@ -970,6 +987,13 @@ struct sqlite3_io_methods { #define SQLITE_FCNTL_SYNC 21 #define SQLITE_FCNTL_COMMIT_PHASETWO 22 #define SQLITE_FCNTL_WIN32_SET_HANDLE 23 +#define SQLITE_FCNTL_WAL_BLOCK 24 + +/* deprecated names */ +#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE +#define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE +#define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO + /* ** CAPI3REF: Mutex Handle @@ -1318,10 +1342,10 @@ struct sqlite3_vfs { ** must return [SQLITE_OK] on success and some other [error code] upon ** failure. */ -SQLITE_API int sqlite3_initialize(void); -SQLITE_API int sqlite3_shutdown(void); -SQLITE_API int sqlite3_os_init(void); -SQLITE_API int sqlite3_os_end(void); +SQLITE_API int SQLITE_STDCALL sqlite3_initialize(void); +SQLITE_API int SQLITE_STDCALL sqlite3_shutdown(void); +SQLITE_API int SQLITE_STDCALL sqlite3_os_init(void); +SQLITE_API int SQLITE_STDCALL sqlite3_os_end(void); /* ** CAPI3REF: Configuring The SQLite Library @@ -1352,10 +1376,11 @@ SQLITE_API int sqlite3_os_end(void); ** ^If the option is unknown or SQLite is unable to set the option ** then this routine returns a non-zero [error code]. */ -SQLITE_API int sqlite3_config(int, ...); +SQLITE_API int SQLITE_CDECL sqlite3_config(int, ...); /* ** CAPI3REF: Configure database connections +** METHOD: sqlite3 ** ** The sqlite3_db_config() interface is used to make configuration ** changes to a [database connection]. The interface is similar to @@ -1370,7 +1395,7 @@ SQLITE_API int sqlite3_config(int, ...); ** ^Calls to sqlite3_db_config() return SQLITE_OK if and only if ** the call is considered successful. */ -SQLITE_API int sqlite3_db_config(sqlite3*, int op, ...); +SQLITE_API int SQLITE_CDECL sqlite3_db_config(sqlite3*, int op, ...); /* ** CAPI3REF: Memory Allocation Routines @@ -1530,7 +1555,7 @@ struct sqlite3_mem_methods { **
  • [sqlite3_memory_used()] **
  • [sqlite3_memory_highwater()] **
  • [sqlite3_soft_heap_limit64()] -**
  • [sqlite3_status()] +**
  • [sqlite3_status64()] **
)^ ** ^Memory allocation statistics are enabled by default unless SQLite is ** compiled with [SQLITE_DEFAULT_MEMSTATUS]=0 in which case memory @@ -1741,7 +1766,6 @@ struct sqlite3_mem_methods { ** compiled for Windows with the [SQLITE_WIN32_MALLOC] pre-processor macro ** defined. ^SQLITE_CONFIG_WIN32_HEAPSIZE takes a 32-bit unsigned integer value ** that specifies the maximum size of the created heap. -** ** ** [[SQLITE_CONFIG_PCACHE_HDRSZ]] **
SQLITE_CONFIG_PCACHE_HDRSZ @@ -1854,15 +1878,17 @@ struct sqlite3_mem_methods { /* ** CAPI3REF: Enable Or Disable Extended Result Codes +** METHOD: sqlite3 ** ** ^The sqlite3_extended_result_codes() routine enables or disables the ** [extended result codes] feature of SQLite. ^The extended result ** codes are disabled by default for historical compatibility. */ -SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff); +SQLITE_API int SQLITE_STDCALL sqlite3_extended_result_codes(sqlite3*, int onoff); /* ** CAPI3REF: Last Insert Rowid +** METHOD: sqlite3 ** ** ^Each entry in most SQLite tables (except for [WITHOUT ROWID] tables) ** has a unique 64-bit signed @@ -1910,10 +1936,11 @@ SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff); ** unpredictable and might not equal either the old or the new ** last insert [rowid]. */ -SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_last_insert_rowid(sqlite3*); /* ** CAPI3REF: Count The Number Of Rows Modified +** METHOD: sqlite3 ** ** ^This function returns the number of rows modified, inserted or ** deleted by the most recently completed INSERT, UPDATE or DELETE @@ -1962,10 +1989,11 @@ SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); ** while [sqlite3_changes()] is running then the value returned ** is unpredictable and not meaningful. */ -SQLITE_API int sqlite3_changes(sqlite3*); +SQLITE_API int SQLITE_STDCALL sqlite3_changes(sqlite3*); /* ** CAPI3REF: Total Number Of Rows Modified +** METHOD: sqlite3 ** ** ^This function returns the total number of rows inserted, modified or ** deleted by all [INSERT], [UPDATE] or [DELETE] statements completed @@ -1985,10 +2013,11 @@ SQLITE_API int sqlite3_changes(sqlite3*); ** while [sqlite3_total_changes()] is running then the value ** returned is unpredictable and not meaningful. */ -SQLITE_API int sqlite3_total_changes(sqlite3*); +SQLITE_API int SQLITE_STDCALL sqlite3_total_changes(sqlite3*); /* ** CAPI3REF: Interrupt A Long-Running Query +** METHOD: sqlite3 ** ** ^This function causes any pending database operation to abort and ** return at its earliest opportunity. This routine is typically @@ -2024,7 +2053,7 @@ SQLITE_API int sqlite3_total_changes(sqlite3*); ** If the database connection closes while [sqlite3_interrupt()] ** is running then bad things will likely happen. */ -SQLITE_API void sqlite3_interrupt(sqlite3*); +SQLITE_API void SQLITE_STDCALL sqlite3_interrupt(sqlite3*); /* ** CAPI3REF: Determine If An SQL Statement Is Complete @@ -2059,12 +2088,13 @@ SQLITE_API void sqlite3_interrupt(sqlite3*); ** The input to [sqlite3_complete16()] must be a zero-terminated ** UTF-16 string in native byte order. */ -SQLITE_API int sqlite3_complete(const char *sql); -SQLITE_API int sqlite3_complete16(const void *sql); +SQLITE_API int SQLITE_STDCALL sqlite3_complete(const char *sql); +SQLITE_API int SQLITE_STDCALL sqlite3_complete16(const void *sql); /* ** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors ** KEYWORDS: {busy-handler callback} {busy handler} +** METHOD: sqlite3 ** ** ^The sqlite3_busy_handler(D,X,P) routine sets a callback function X ** that might be invoked with argument P whenever @@ -2120,10 +2150,11 @@ SQLITE_API int sqlite3_complete16(const void *sql); ** A busy handler must not close the database connection ** or [prepared statement] that invoked the busy handler. */ -SQLITE_API int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); +SQLITE_API int SQLITE_STDCALL sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); /* ** CAPI3REF: Set A Busy Timeout +** METHOD: sqlite3 ** ** ^This routine sets a [sqlite3_busy_handler | busy handler] that sleeps ** for a specified amount of time when a table is locked. ^The handler @@ -2142,10 +2173,11 @@ SQLITE_API int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); ** ** See also: [PRAGMA busy_timeout] */ -SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); +SQLITE_API int SQLITE_STDCALL sqlite3_busy_timeout(sqlite3*, int ms); /* ** CAPI3REF: Convenience Routines For Running Queries +** METHOD: sqlite3 ** ** This is a legacy interface that is preserved for backwards compatibility. ** Use of this interface is not recommended. @@ -2216,7 +2248,7 @@ SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); ** reflected in subsequent calls to [sqlite3_errcode()] or ** [sqlite3_errmsg()]. */ -SQLITE_API int sqlite3_get_table( +SQLITE_API int SQLITE_STDCALL sqlite3_get_table( sqlite3 *db, /* An open database */ const char *zSql, /* SQL to be evaluated */ char ***pazResult, /* Results of the query */ @@ -2224,13 +2256,17 @@ SQLITE_API int sqlite3_get_table( int *pnColumn, /* Number of result columns written here */ char **pzErrmsg /* Error msg written here */ ); -SQLITE_API void sqlite3_free_table(char **result); +SQLITE_API void SQLITE_STDCALL sqlite3_free_table(char **result); /* ** CAPI3REF: Formatted String Printing Functions ** ** These routines are work-alikes of the "printf()" family of functions ** from the standard C library. +** These routines understand most of the common K&R formatting options, +** plus some additional non-standard formats, detailed below. +** Note that some of the more obscure formatting options from recent +** C-library standards are omitted from this implementation. ** ** ^The sqlite3_mprintf() and sqlite3_vmprintf() routines write their ** results into memory obtained from [sqlite3_malloc()]. @@ -2263,7 +2299,7 @@ SQLITE_API void sqlite3_free_table(char **result); ** These routines all implement some additional formatting ** options that are useful for constructing SQL statements. ** All of the usual printf() formatting options apply. In addition, there -** is are "%q", "%Q", and "%z" options. +** is are "%q", "%Q", "%w" and "%z" options. ** ** ^(The %q option works like %s in that it substitutes a nul-terminated ** string from the argument list. But %q also doubles every '\'' character. @@ -2316,14 +2352,20 @@ SQLITE_API void sqlite3_free_table(char **result); ** The code above will render a correct SQL statement in the zSQL ** variable even if the zText variable is a NULL pointer. ** +** ^(The "%w" formatting option is like "%q" except that it expects to +** be contained within double-quotes instead of single quotes, and it +** escapes the double-quote character instead of the single-quote +** character.)^ The "%w" formatting option is intended for safely inserting +** table and column names into a constructed SQL statement. +** ** ^(The "%z" formatting option works like "%s" but with the ** addition that after the string has been read and copied into ** the result, [sqlite3_free()] is called on the input string.)^ */ -SQLITE_API char *sqlite3_mprintf(const char*,...); -SQLITE_API char *sqlite3_vmprintf(const char*, va_list); -SQLITE_API char *sqlite3_snprintf(int,char*,const char*, ...); -SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list); +SQLITE_API char *SQLITE_CDECL sqlite3_mprintf(const char*,...); +SQLITE_API char *SQLITE_STDCALL sqlite3_vmprintf(const char*, va_list); +SQLITE_API char *SQLITE_CDECL sqlite3_snprintf(int,char*,const char*, ...); +SQLITE_API char *SQLITE_STDCALL sqlite3_vsnprintf(int,char*,const char*, va_list); /* ** CAPI3REF: Memory Allocation Subsystem @@ -2413,12 +2455,12 @@ SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list); ** a block of memory after it has been released using ** [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*); +SQLITE_API void *SQLITE_STDCALL sqlite3_malloc(int); +SQLITE_API void *SQLITE_STDCALL sqlite3_malloc64(sqlite3_uint64); +SQLITE_API void *SQLITE_STDCALL sqlite3_realloc(void*, int); +SQLITE_API void *SQLITE_STDCALL sqlite3_realloc64(void*, sqlite3_uint64); +SQLITE_API void SQLITE_STDCALL sqlite3_free(void*); +SQLITE_API sqlite3_uint64 SQLITE_STDCALL sqlite3_msize(void*); /* ** CAPI3REF: Memory Allocator Statistics @@ -2443,8 +2485,8 @@ SQLITE_API sqlite3_uint64 sqlite3_msize(void*); ** by [sqlite3_memory_highwater(1)] is the high-water mark ** prior to the reset. */ -SQLITE_API sqlite3_int64 sqlite3_memory_used(void); -SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_memory_used(void); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_memory_highwater(int resetFlag); /* ** CAPI3REF: Pseudo-Random Number Generator @@ -2467,10 +2509,11 @@ SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag); ** internally and without recourse to the [sqlite3_vfs] xRandomness ** method. */ -SQLITE_API void sqlite3_randomness(int N, void *P); +SQLITE_API void SQLITE_STDCALL sqlite3_randomness(int N, void *P); /* ** CAPI3REF: Compile-Time Authorization Callbacks +** METHOD: sqlite3 ** ** ^This routine registers an authorizer callback with a particular ** [database connection], supplied in the first argument. @@ -2549,7 +2592,7 @@ SQLITE_API void sqlite3_randomness(int N, void *P); ** as stated in the previous paragraph, sqlite3_step() invokes ** sqlite3_prepare_v2() to reprepare a statement after a schema change. */ -SQLITE_API int sqlite3_set_authorizer( +SQLITE_API int SQLITE_STDCALL sqlite3_set_authorizer( sqlite3*, int (*xAuth)(void*,int,const char*,const char*,const char*,const char*), void *pUserData @@ -2627,6 +2670,7 @@ SQLITE_API int sqlite3_set_authorizer( /* ** CAPI3REF: Tracing And Profiling Functions +** METHOD: sqlite3 ** ** These routines register callback functions that can be used for ** tracing and profiling the execution of SQL statements. @@ -2653,12 +2697,13 @@ SQLITE_API int sqlite3_set_authorizer( ** sqlite3_profile() function is considered experimental and is ** subject to change in future versions of SQLite. */ -SQLITE_API void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); -SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*, +SQLITE_API void *SQLITE_STDCALL sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); +SQLITE_API SQLITE_EXPERIMENTAL void *SQLITE_STDCALL sqlite3_profile(sqlite3*, void(*xProfile)(void*,const char*,sqlite3_uint64), void*); /* ** CAPI3REF: Query Progress Callbacks +** METHOD: sqlite3 ** ** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback ** function X to be invoked periodically during long running calls to @@ -2688,10 +2733,11 @@ SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*, ** database connections for the meaning of "modify" in this paragraph. ** */ -SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); +SQLITE_API void SQLITE_STDCALL sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); /* ** CAPI3REF: Opening A New Database Connection +** CONSTRUCTOR: sqlite3 ** ** ^These routines open an SQLite database file as specified by the ** filename argument. ^The filename argument is interpreted as UTF-8 for @@ -2916,15 +2962,15 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** ** See also: [sqlite3_temp_directory] */ -SQLITE_API int sqlite3_open( +SQLITE_API int SQLITE_STDCALL sqlite3_open( const char *filename, /* Database filename (UTF-8) */ sqlite3 **ppDb /* OUT: SQLite db handle */ ); -SQLITE_API int sqlite3_open16( +SQLITE_API int SQLITE_STDCALL sqlite3_open16( const void *filename, /* Database filename (UTF-16) */ sqlite3 **ppDb /* OUT: SQLite db handle */ ); -SQLITE_API int sqlite3_open_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_open_v2( const char *filename, /* Database filename (UTF-8) */ sqlite3 **ppDb, /* OUT: SQLite db handle */ int flags, /* Flags */ @@ -2970,19 +3016,22 @@ SQLITE_API int sqlite3_open_v2( ** VFS method, then the behavior of this routine is undefined and probably ** undesirable. */ -SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); -SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); -SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64); +SQLITE_API const char *SQLITE_STDCALL sqlite3_uri_parameter(const char *zFilename, const char *zParam); +SQLITE_API int SQLITE_STDCALL sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_uri_int64(const char*, const char*, sqlite3_int64); /* ** CAPI3REF: Error Codes And Messages +** METHOD: sqlite3 ** -** ^The sqlite3_errcode() interface returns the numeric [result code] or -** [extended result code] for the most recent failed sqlite3_* API call -** associated with a [database connection]. If a prior API call failed -** but the most recent API call succeeded, the return value from -** sqlite3_errcode() is undefined. ^The sqlite3_extended_errcode() +** ^If the most recent sqlite3_* API call associated with +** [database connection] D failed, then the sqlite3_errcode(D) interface +** returns the numeric [result code] or [extended result code] for that +** API call. +** If the most recent API call was successful, +** then the return value from sqlite3_errcode() is undefined. +** ^The sqlite3_extended_errcode() ** interface is the same except that it always returns the ** [extended result code] even when extended result codes are ** disabled. @@ -3013,40 +3062,41 @@ SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int ** was invoked incorrectly by the application. In that case, the ** error code and message may or may not be set. */ -SQLITE_API int sqlite3_errcode(sqlite3 *db); -SQLITE_API int sqlite3_extended_errcode(sqlite3 *db); -SQLITE_API const char *sqlite3_errmsg(sqlite3*); -SQLITE_API const void *sqlite3_errmsg16(sqlite3*); -SQLITE_API const char *sqlite3_errstr(int); +SQLITE_API int SQLITE_STDCALL sqlite3_errcode(sqlite3 *db); +SQLITE_API int SQLITE_STDCALL sqlite3_extended_errcode(sqlite3 *db); +SQLITE_API const char *SQLITE_STDCALL sqlite3_errmsg(sqlite3*); +SQLITE_API const void *SQLITE_STDCALL sqlite3_errmsg16(sqlite3*); +SQLITE_API const char *SQLITE_STDCALL sqlite3_errstr(int); /* -** CAPI3REF: SQL Statement Object +** CAPI3REF: Prepared Statement Object ** KEYWORDS: {prepared statement} {prepared statements} ** -** An instance of this object represents a single SQL statement. -** This object is variously known as a "prepared statement" or a -** "compiled SQL statement" or simply as a "statement". +** An instance of this object represents a single SQL statement that +** has been compiled into binary form and is ready to be evaluated. ** -** The life of a statement object goes something like this: +** Think of each SQL statement as a separate computer program. The +** original SQL text is source code. A prepared statement object +** is the compiled object code. All SQL must be converted into a +** prepared statement before it can be run. +** +** The life-cycle of a prepared statement object usually goes like this: ** **
    -**
  1. Create the object using [sqlite3_prepare_v2()] or a related -** function. -**
  2. Bind values to [host parameters] using the sqlite3_bind_*() +**
  3. Create the prepared statement object using [sqlite3_prepare_v2()]. +**
  4. Bind values to [parameters] using the sqlite3_bind_*() ** interfaces. **
  5. Run the SQL by calling [sqlite3_step()] one or more times. -**
  6. Reset the statement using [sqlite3_reset()] then go back +**
  7. Reset the prepared statement using [sqlite3_reset()] then go back ** to step 2. Do this zero or more times. **
  8. Destroy the object using [sqlite3_finalize()]. **
-** -** Refer to documentation on individual methods above for additional -** information. */ typedef struct sqlite3_stmt sqlite3_stmt; /* ** CAPI3REF: Run-time Limits +** METHOD: sqlite3 ** ** ^(This interface allows the size of various constructs to be limited ** on a connection by connection basis. The first parameter is the @@ -3084,7 +3134,7 @@ typedef struct sqlite3_stmt sqlite3_stmt; ** ** New run-time limit categories may be added in future releases. */ -SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); +SQLITE_API int SQLITE_STDCALL sqlite3_limit(sqlite3*, int id, int newVal); /* ** CAPI3REF: Run-Time Limit Categories @@ -3158,6 +3208,8 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); /* ** CAPI3REF: Compiling An SQL Statement ** KEYWORDS: {SQL statement compiler} +** METHOD: sqlite3 +** CONSTRUCTOR: sqlite3_stmt ** ** To execute an SQL query, it must first be compiled into a byte-code ** program using one of these routines. @@ -3171,16 +3223,14 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** interfaces use UTF-8, and sqlite3_prepare16() and sqlite3_prepare16_v2() ** use UTF-16. ** -** ^If the nByte argument is less than zero, then zSql is read up to the -** first zero terminator. ^If nByte is non-negative, then it is the maximum -** number of bytes read from zSql. ^When nByte is non-negative, the -** zSql string ends at either the first '\000' or '\u0000' character or -** the nByte-th byte, whichever comes first. If the caller knows -** that the supplied string is nul-terminated, then there is a small -** performance advantage to be gained by passing an nByte parameter that -** is equal to the number of bytes in the input string including -** the nul-terminator bytes as this saves SQLite from having to -** make a copy of the input string. +** ^If the nByte argument is negative, then zSql is read up to the +** first zero terminator. ^If nByte is positive, then it is the +** number of bytes read from zSql. ^If nByte is zero, then no prepared +** statement is generated. +** If the caller knows that the supplied string is nul-terminated, then +** there is a small performance advantage to passing an nByte parameter that +** is the number of bytes in the input string including +** the nul-terminator. ** ** ^If pzTail is not NULL then *pzTail is made to point to the first byte ** past the end of the first SQL statement in zSql. These routines only @@ -3236,28 +3286,28 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** ** */ -SQLITE_API int sqlite3_prepare( +SQLITE_API int SQLITE_STDCALL sqlite3_prepare( sqlite3 *db, /* Database handle */ const char *zSql, /* SQL statement, UTF-8 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const char **pzTail /* OUT: Pointer to unused portion of zSql */ ); -SQLITE_API int sqlite3_prepare_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_prepare_v2( sqlite3 *db, /* Database handle */ const char *zSql, /* SQL statement, UTF-8 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const char **pzTail /* OUT: Pointer to unused portion of zSql */ ); -SQLITE_API int sqlite3_prepare16( +SQLITE_API int SQLITE_STDCALL sqlite3_prepare16( sqlite3 *db, /* Database handle */ const void *zSql, /* SQL statement, UTF-16 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const void **pzTail /* OUT: Pointer to unused portion of zSql */ ); -SQLITE_API int sqlite3_prepare16_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_prepare16_v2( sqlite3 *db, /* Database handle */ const void *zSql, /* SQL statement, UTF-16 encoded */ int nByte, /* Maximum length of zSql in bytes. */ @@ -3267,15 +3317,17 @@ SQLITE_API int sqlite3_prepare16_v2( /* ** CAPI3REF: Retrieving Statement SQL +** METHOD: sqlite3_stmt ** ** ^This interface can be used to retrieve a saved copy of the original ** SQL text used to create a [prepared statement] if that statement was ** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()]. */ -SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); +SQLITE_API const char *SQLITE_STDCALL sqlite3_sql(sqlite3_stmt *pStmt); /* ** CAPI3REF: Determine If An SQL Statement Writes The Database +** METHOD: sqlite3_stmt ** ** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if ** and only if the [prepared statement] X makes no direct changes to @@ -3303,10 +3355,11 @@ SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); ** change the configuration of a database connection, they do not make ** changes to the content of the database files on disk. */ -SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); +SQLITE_API int SQLITE_STDCALL sqlite3_stmt_readonly(sqlite3_stmt *pStmt); /* ** CAPI3REF: Determine If A Prepared Statement Has Been Reset +** METHOD: sqlite3_stmt ** ** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the ** [prepared statement] S has been stepped at least once using @@ -3322,7 +3375,7 @@ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); ** for example, in diagnostic routines to search for prepared ** statements that are holding a transaction open. */ -SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*); +SQLITE_API int SQLITE_STDCALL sqlite3_stmt_busy(sqlite3_stmt*); /* ** CAPI3REF: Dynamically Typed Value Object @@ -3381,6 +3434,7 @@ typedef struct sqlite3_context sqlite3_context; ** CAPI3REF: Binding Values To Prepared Statements ** KEYWORDS: {host parameter} {host parameters} {host parameter name} ** KEYWORDS: {SQL parameter} {SQL parameters} {parameter binding} +** METHOD: sqlite3_stmt ** ** ^(In the SQL statement text input to [sqlite3_prepare_v2()] and its variants, ** literals may be replaced by a [parameter] that matches one of following @@ -3483,22 +3537,23 @@ typedef struct sqlite3_context sqlite3_context; ** See also: [sqlite3_bind_parameter_count()], ** [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, +SQLITE_API int SQLITE_STDCALL sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); +SQLITE_API int SQLITE_STDCALL 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,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, +SQLITE_API int SQLITE_STDCALL sqlite3_bind_double(sqlite3_stmt*, int, double); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_int(sqlite3_stmt*, int, int); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_null(sqlite3_stmt*, int); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*)); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*)); +SQLITE_API int SQLITE_STDCALL 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); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); /* ** CAPI3REF: Number Of SQL Parameters +** METHOD: sqlite3_stmt ** ** ^This routine can be used to find the number of [SQL parameters] ** in a [prepared statement]. SQL parameters are tokens of the @@ -3515,10 +3570,11 @@ SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); ** [sqlite3_bind_parameter_name()], and ** [sqlite3_bind_parameter_index()]. */ -SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt*); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_parameter_count(sqlite3_stmt*); /* ** CAPI3REF: Name Of A Host Parameter +** METHOD: sqlite3_stmt ** ** ^The sqlite3_bind_parameter_name(P,N) interface returns ** the name of the N-th [SQL parameter] in the [prepared statement] P. @@ -3542,10 +3598,11 @@ SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt*); ** [sqlite3_bind_parameter_count()], and ** [sqlite3_bind_parameter_index()]. */ -SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); +SQLITE_API const char *SQLITE_STDCALL sqlite3_bind_parameter_name(sqlite3_stmt*, int); /* ** CAPI3REF: Index Of A Parameter With A Given Name +** METHOD: sqlite3_stmt ** ** ^Return the index of an SQL parameter given its name. ^The ** index value returned is suitable for use as the second @@ -3558,19 +3615,21 @@ SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); ** [sqlite3_bind_parameter_count()], and ** [sqlite3_bind_parameter_index()]. */ -SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); /* ** CAPI3REF: Reset All Bindings On A Prepared Statement +** METHOD: sqlite3_stmt ** ** ^Contrary to the intuition of many, [sqlite3_reset()] does not reset ** the [sqlite3_bind_blob | bindings] on a [prepared statement]. ** ^Use this routine to reset all host parameters to NULL. */ -SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*); +SQLITE_API int SQLITE_STDCALL sqlite3_clear_bindings(sqlite3_stmt*); /* ** CAPI3REF: Number Of Columns In A Result Set +** METHOD: sqlite3_stmt ** ** ^Return the number of columns in the result set returned by the ** [prepared statement]. ^This routine returns 0 if pStmt is an SQL @@ -3578,10 +3637,11 @@ SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*); ** ** See also: [sqlite3_data_count()] */ -SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt); +SQLITE_API int SQLITE_STDCALL sqlite3_column_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Column Names In A Result Set +** METHOD: sqlite3_stmt ** ** ^These routines return the name assigned to a particular column ** in the result set of a [SELECT] statement. ^The sqlite3_column_name() @@ -3606,11 +3666,12 @@ SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt); ** then the name of the column is unspecified and may change from ** one release of SQLite to the next. */ -SQLITE_API const char *sqlite3_column_name(sqlite3_stmt*, int N); -SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_name(sqlite3_stmt*, int N); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_name16(sqlite3_stmt*, int N); /* ** CAPI3REF: Source Of Data In A Query Result +** METHOD: sqlite3_stmt ** ** ^These routines provide a means to determine the database, table, and ** table column that is the origin of a particular result column in @@ -3654,15 +3715,16 @@ SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); ** for the same [prepared statement] and result column ** at the same time then the results are undefined. */ -SQLITE_API const char *sqlite3_column_database_name(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt*,int); -SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt*,int); -SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt*,int); +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_database_name(sqlite3_stmt*,int); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_database_name16(sqlite3_stmt*,int); +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_table_name(sqlite3_stmt*,int); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_table_name16(sqlite3_stmt*,int); +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_origin_name(sqlite3_stmt*,int); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_origin_name16(sqlite3_stmt*,int); /* ** CAPI3REF: Declared Datatype Of A Query Result +** METHOD: sqlite3_stmt ** ** ^(The first parameter is a [prepared statement]. ** If this statement is a [SELECT] statement and the Nth column of the @@ -3690,11 +3752,12 @@ SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt*,int); ** is associated with individual values, not with the containers ** used to hold those values. */ -SQLITE_API const char *sqlite3_column_decltype(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_decltype(sqlite3_stmt*,int); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_decltype16(sqlite3_stmt*,int); /* ** CAPI3REF: Evaluate An SQL Statement +** METHOD: sqlite3_stmt ** ** After a [prepared statement] has been prepared using either ** [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] or one of the legacy @@ -3770,10 +3833,11 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); ** then the more specific [error codes] are returned directly ** by sqlite3_step(). The use of the "v2" interface is recommended. */ -SQLITE_API int sqlite3_step(sqlite3_stmt*); +SQLITE_API int SQLITE_STDCALL sqlite3_step(sqlite3_stmt*); /* ** CAPI3REF: Number of columns in a result set +** METHOD: sqlite3_stmt ** ** ^The sqlite3_data_count(P) interface returns the number of columns in the ** current row of the result set of [prepared statement] P. @@ -3790,7 +3854,7 @@ SQLITE_API int sqlite3_step(sqlite3_stmt*); ** ** See also: [sqlite3_column_count()] */ -SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); +SQLITE_API int SQLITE_STDCALL sqlite3_data_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Fundamental Datatypes @@ -3827,6 +3891,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Result Values From A Query ** KEYWORDS: {column access functions} +** METHOD: sqlite3_stmt ** ** These routines form the "result set" interface. ** @@ -3986,19 +4051,20 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); ** pointer. Subsequent calls to [sqlite3_errcode()] will return ** [SQLITE_NOMEM].)^ */ -SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_bytes(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_bytes16(sqlite3_stmt*, int iCol); -SQLITE_API double sqlite3_column_double(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_int(sqlite3_stmt*, int iCol); -SQLITE_API sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); -SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); -SQLITE_API const void *sqlite3_column_text16(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol); -SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_blob(sqlite3_stmt*, int iCol); +SQLITE_API int SQLITE_STDCALL sqlite3_column_bytes(sqlite3_stmt*, int iCol); +SQLITE_API int SQLITE_STDCALL sqlite3_column_bytes16(sqlite3_stmt*, int iCol); +SQLITE_API double SQLITE_STDCALL sqlite3_column_double(sqlite3_stmt*, int iCol); +SQLITE_API int SQLITE_STDCALL sqlite3_column_int(sqlite3_stmt*, int iCol); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_column_int64(sqlite3_stmt*, int iCol); +SQLITE_API const unsigned char *SQLITE_STDCALL sqlite3_column_text(sqlite3_stmt*, int iCol); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_text16(sqlite3_stmt*, int iCol); +SQLITE_API int SQLITE_STDCALL sqlite3_column_type(sqlite3_stmt*, int iCol); +SQLITE_API sqlite3_value *SQLITE_STDCALL sqlite3_column_value(sqlite3_stmt*, int iCol); /* ** CAPI3REF: Destroy A Prepared Statement Object +** DESTRUCTOR: sqlite3_stmt ** ** ^The sqlite3_finalize() function is called to delete a [prepared statement]. ** ^If the most recent evaluation of the statement encountered no errors @@ -4022,10 +4088,11 @@ SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); ** statement after it has been finalized can result in undefined and ** undesirable behavior such as segfaults and heap corruption. */ -SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); +SQLITE_API int SQLITE_STDCALL sqlite3_finalize(sqlite3_stmt *pStmt); /* ** CAPI3REF: Reset A Prepared Statement Object +** METHOD: sqlite3_stmt ** ** The sqlite3_reset() function is called to reset a [prepared statement] ** object back to its initial state, ready to be re-executed. @@ -4048,13 +4115,14 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); ** ^The [sqlite3_reset(S)] interface does not change the values ** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S. */ -SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); +SQLITE_API int SQLITE_STDCALL sqlite3_reset(sqlite3_stmt *pStmt); /* ** CAPI3REF: Create Or Redefine SQL Functions ** KEYWORDS: {function creation routines} ** KEYWORDS: {application-defined SQL function} ** KEYWORDS: {application-defined SQL functions} +** METHOD: sqlite3 ** ** ^These functions (collectively known as "function creation routines") ** are used to add SQL functions or aggregates or to redefine the behavior @@ -4147,7 +4215,7 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); ** close the database connection nor finalize or reset the prepared ** statement in which the function is running. */ -SQLITE_API int sqlite3_create_function( +SQLITE_API int SQLITE_STDCALL sqlite3_create_function( sqlite3 *db, const char *zFunctionName, int nArg, @@ -4157,7 +4225,7 @@ SQLITE_API int sqlite3_create_function( void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*) ); -SQLITE_API int sqlite3_create_function16( +SQLITE_API int SQLITE_STDCALL sqlite3_create_function16( sqlite3 *db, const void *zFunctionName, int nArg, @@ -4167,7 +4235,7 @@ SQLITE_API int sqlite3_create_function16( void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*) ); -SQLITE_API int sqlite3_create_function_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_create_function_v2( sqlite3 *db, const char *zFunctionName, int nArg, @@ -4209,21 +4277,22 @@ SQLITE_API int sqlite3_create_function_v2( ** These functions are [deprecated]. In order to maintain ** backwards compatibility with older code, these functions continue ** to be supported. However, new applications should avoid -** the use of these functions. To help encourage people to avoid -** using these functions, we are not going to tell you what they do. +** the use of these functions. To encourage programmers to avoid +** these functions, we will not explain what they do. */ #ifndef SQLITE_OMIT_DEPRECATED -SQLITE_API SQLITE_DEPRECATED int sqlite3_aggregate_count(sqlite3_context*); -SQLITE_API SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*); -SQLITE_API SQLITE_DEPRECATED int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*); -SQLITE_API SQLITE_DEPRECATED int sqlite3_global_recover(void); -SQLITE_API SQLITE_DEPRECATED void sqlite3_thread_cleanup(void); -SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int), +SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_aggregate_count(sqlite3_context*); +SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_expired(sqlite3_stmt*); +SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*); +SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_global_recover(void); +SQLITE_API SQLITE_DEPRECATED void SQLITE_STDCALL sqlite3_thread_cleanup(void); +SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int), void*,sqlite3_int64); #endif /* ** CAPI3REF: Obtaining SQL Function Parameter Values +** METHOD: sqlite3_value ** ** The C-language implementation of SQL functions and aggregates uses ** this set of interface routines to access the parameter values on @@ -4267,21 +4336,22 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6 ** These routines must be called from the same thread as ** the SQL function that supplied the [sqlite3_value*] parameters. */ -SQLITE_API const void *sqlite3_value_blob(sqlite3_value*); -SQLITE_API int sqlite3_value_bytes(sqlite3_value*); -SQLITE_API int sqlite3_value_bytes16(sqlite3_value*); -SQLITE_API double sqlite3_value_double(sqlite3_value*); -SQLITE_API int sqlite3_value_int(sqlite3_value*); -SQLITE_API sqlite3_int64 sqlite3_value_int64(sqlite3_value*); -SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value*); -SQLITE_API const void *sqlite3_value_text16(sqlite3_value*); -SQLITE_API const void *sqlite3_value_text16le(sqlite3_value*); -SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*); -SQLITE_API int sqlite3_value_type(sqlite3_value*); -SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); +SQLITE_API const void *SQLITE_STDCALL sqlite3_value_blob(sqlite3_value*); +SQLITE_API int SQLITE_STDCALL sqlite3_value_bytes(sqlite3_value*); +SQLITE_API int SQLITE_STDCALL sqlite3_value_bytes16(sqlite3_value*); +SQLITE_API double SQLITE_STDCALL sqlite3_value_double(sqlite3_value*); +SQLITE_API int SQLITE_STDCALL sqlite3_value_int(sqlite3_value*); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_value_int64(sqlite3_value*); +SQLITE_API const unsigned char *SQLITE_STDCALL sqlite3_value_text(sqlite3_value*); +SQLITE_API const void *SQLITE_STDCALL sqlite3_value_text16(sqlite3_value*); +SQLITE_API const void *SQLITE_STDCALL sqlite3_value_text16le(sqlite3_value*); +SQLITE_API const void *SQLITE_STDCALL sqlite3_value_text16be(sqlite3_value*); +SQLITE_API int SQLITE_STDCALL sqlite3_value_type(sqlite3_value*); +SQLITE_API int SQLITE_STDCALL sqlite3_value_numeric_type(sqlite3_value*); /* ** CAPI3REF: Obtain Aggregate Function Context +** METHOD: sqlite3_context ** ** Implementations of aggregate SQL functions use this ** routine to allocate memory for storing their state. @@ -4322,10 +4392,11 @@ SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); ** This routine must be called from the same thread in which ** the aggregate SQL function is running. */ -SQLITE_API void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); +SQLITE_API void *SQLITE_STDCALL sqlite3_aggregate_context(sqlite3_context*, int nBytes); /* ** CAPI3REF: User Data For Functions +** METHOD: sqlite3_context ** ** ^The sqlite3_user_data() interface returns a copy of ** the pointer that was the pUserData parameter (the 5th parameter) @@ -4336,10 +4407,11 @@ SQLITE_API void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); ** This routine must be called from the same thread in which ** the application-defined function is running. */ -SQLITE_API void *sqlite3_user_data(sqlite3_context*); +SQLITE_API void *SQLITE_STDCALL sqlite3_user_data(sqlite3_context*); /* ** CAPI3REF: Database Connection For Functions +** METHOD: sqlite3_context ** ** ^The sqlite3_context_db_handle() interface returns a copy of ** the pointer to the [database connection] (the 1st parameter) @@ -4347,10 +4419,11 @@ SQLITE_API void *sqlite3_user_data(sqlite3_context*); ** and [sqlite3_create_function16()] routines that originally ** registered the application defined function. */ -SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); +SQLITE_API sqlite3 *SQLITE_STDCALL sqlite3_context_db_handle(sqlite3_context*); /* ** CAPI3REF: Function Auxiliary Data +** METHOD: sqlite3_context ** ** These functions may be used by (non-aggregate) SQL functions to ** associate metadata with argument values. If the same value is passed to @@ -4399,8 +4472,8 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); ** These routines must be called from the same thread in which ** the SQL function is running. */ -SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); -SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); +SQLITE_API void *SQLITE_STDCALL sqlite3_get_auxdata(sqlite3_context*, int N); +SQLITE_API void SQLITE_STDCALL sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); /* @@ -4423,6 +4496,7 @@ typedef void (*sqlite3_destructor_type)(void*); /* ** CAPI3REF: Setting The Result Of An SQL Function +** METHOD: sqlite3_context ** ** These routines are used by the xFunc or xFinal callbacks that ** implement SQL functions and aggregates. See @@ -4535,29 +4609,30 @@ typedef void (*sqlite3_destructor_type)(void*); ** than the one containing the application-defined function that received ** 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*, +SQLITE_API void SQLITE_STDCALL sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*)); +SQLITE_API void SQLITE_STDCALL 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); -SQLITE_API void sqlite3_result_error_toobig(sqlite3_context*); -SQLITE_API void sqlite3_result_error_nomem(sqlite3_context*); -SQLITE_API void sqlite3_result_error_code(sqlite3_context*, int); -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, +SQLITE_API void SQLITE_STDCALL sqlite3_result_double(sqlite3_context*, double); +SQLITE_API void SQLITE_STDCALL sqlite3_result_error(sqlite3_context*, const char*, int); +SQLITE_API void SQLITE_STDCALL sqlite3_result_error16(sqlite3_context*, const void*, int); +SQLITE_API void SQLITE_STDCALL sqlite3_result_error_toobig(sqlite3_context*); +SQLITE_API void SQLITE_STDCALL sqlite3_result_error_nomem(sqlite3_context*); +SQLITE_API void SQLITE_STDCALL sqlite3_result_error_code(sqlite3_context*, int); +SQLITE_API void SQLITE_STDCALL sqlite3_result_int(sqlite3_context*, int); +SQLITE_API void SQLITE_STDCALL sqlite3_result_int64(sqlite3_context*, sqlite3_int64); +SQLITE_API void SQLITE_STDCALL sqlite3_result_null(sqlite3_context*); +SQLITE_API void SQLITE_STDCALL sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); +SQLITE_API void SQLITE_STDCALL 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*)); -SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); -SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); +SQLITE_API void SQLITE_STDCALL sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); +SQLITE_API void SQLITE_STDCALL sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); +SQLITE_API void SQLITE_STDCALL sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); +SQLITE_API void SQLITE_STDCALL sqlite3_result_value(sqlite3_context*, sqlite3_value*); +SQLITE_API void SQLITE_STDCALL sqlite3_result_zeroblob(sqlite3_context*, int n); /* ** CAPI3REF: Define New Collating Sequences +** METHOD: sqlite3 ** ** ^These functions add, remove, or modify a [collation] associated ** with the [database connection] specified as the first argument. @@ -4635,14 +4710,14 @@ SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); ** ** See also: [sqlite3_collation_needed()] and [sqlite3_collation_needed16()]. */ -SQLITE_API int sqlite3_create_collation( +SQLITE_API int SQLITE_STDCALL sqlite3_create_collation( sqlite3*, const char *zName, int eTextRep, void *pArg, int(*xCompare)(void*,int,const void*,int,const void*) ); -SQLITE_API int sqlite3_create_collation_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_create_collation_v2( sqlite3*, const char *zName, int eTextRep, @@ -4650,7 +4725,7 @@ SQLITE_API int sqlite3_create_collation_v2( int(*xCompare)(void*,int,const void*,int,const void*), void(*xDestroy)(void*) ); -SQLITE_API int sqlite3_create_collation16( +SQLITE_API int SQLITE_STDCALL sqlite3_create_collation16( sqlite3*, const void *zName, int eTextRep, @@ -4660,6 +4735,7 @@ SQLITE_API int sqlite3_create_collation16( /* ** CAPI3REF: Collation Needed Callbacks +** METHOD: sqlite3 ** ** ^To avoid having to register all collation sequences before a database ** can be used, a single callback function may be registered with the @@ -4684,12 +4760,12 @@ SQLITE_API int sqlite3_create_collation16( ** [sqlite3_create_collation()], [sqlite3_create_collation16()], or ** [sqlite3_create_collation_v2()]. */ -SQLITE_API int sqlite3_collation_needed( +SQLITE_API int SQLITE_STDCALL sqlite3_collation_needed( sqlite3*, void*, void(*)(void*,sqlite3*,int eTextRep,const char*) ); -SQLITE_API int sqlite3_collation_needed16( +SQLITE_API int SQLITE_STDCALL sqlite3_collation_needed16( sqlite3*, void*, void(*)(void*,sqlite3*,int eTextRep,const void*) @@ -4703,11 +4779,11 @@ SQLITE_API int sqlite3_collation_needed16( ** The code to implement this API is not available in the public release ** of SQLite. */ -SQLITE_API int sqlite3_key( +SQLITE_API int SQLITE_STDCALL sqlite3_key( sqlite3 *db, /* Database to be rekeyed */ const void *pKey, int nKey /* The key */ ); -SQLITE_API int sqlite3_key_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_key_v2( sqlite3 *db, /* Database to be rekeyed */ const char *zDbName, /* Name of the database */ const void *pKey, int nKey /* The key */ @@ -4721,11 +4797,11 @@ SQLITE_API int sqlite3_key_v2( ** The code to implement this API is not available in the public release ** of SQLite. */ -SQLITE_API int sqlite3_rekey( +SQLITE_API int SQLITE_STDCALL sqlite3_rekey( sqlite3 *db, /* Database to be rekeyed */ const void *pKey, int nKey /* The new key */ ); -SQLITE_API int sqlite3_rekey_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_rekey_v2( sqlite3 *db, /* Database to be rekeyed */ const char *zDbName, /* Name of the database */ const void *pKey, int nKey /* The new key */ @@ -4735,7 +4811,7 @@ SQLITE_API int sqlite3_rekey_v2( ** Specify the activation key for a SEE database. Unless ** activated, none of the SEE routines will work. */ -SQLITE_API void sqlite3_activate_see( +SQLITE_API void SQLITE_STDCALL sqlite3_activate_see( const char *zPassPhrase /* Activation phrase */ ); #endif @@ -4745,7 +4821,7 @@ SQLITE_API void sqlite3_activate_see( ** Specify the activation key for a CEROD database. Unless ** activated, none of the CEROD routines will work. */ -SQLITE_API void sqlite3_activate_cerod( +SQLITE_API void SQLITE_STDCALL sqlite3_activate_cerod( const char *zPassPhrase /* Activation phrase */ ); #endif @@ -4767,7 +4843,7 @@ SQLITE_API void sqlite3_activate_cerod( ** all, then the behavior of sqlite3_sleep() may deviate from the description ** in the previous paragraphs. */ -SQLITE_API int sqlite3_sleep(int); +SQLITE_API int SQLITE_STDCALL sqlite3_sleep(int); /* ** CAPI3REF: Name Of The Folder Holding Temporary Files @@ -4867,6 +4943,7 @@ SQLITE_API SQLITE_EXTERN char *sqlite3_data_directory; /* ** CAPI3REF: Test For Auto-Commit Mode ** KEYWORDS: {autocommit mode} +** METHOD: sqlite3 ** ** ^The sqlite3_get_autocommit() interface returns non-zero or ** zero if the given database connection is or is not in autocommit mode, @@ -4885,10 +4962,11 @@ SQLITE_API SQLITE_EXTERN char *sqlite3_data_directory; ** connection while this routine is running, then the return value ** is undefined. */ -SQLITE_API int sqlite3_get_autocommit(sqlite3*); +SQLITE_API int SQLITE_STDCALL sqlite3_get_autocommit(sqlite3*); /* ** CAPI3REF: Find The Database Handle Of A Prepared Statement +** METHOD: sqlite3_stmt ** ** ^The sqlite3_db_handle interface returns the [database connection] handle ** to which a [prepared statement] belongs. ^The [database connection] @@ -4897,10 +4975,11 @@ SQLITE_API int sqlite3_get_autocommit(sqlite3*); ** to the [sqlite3_prepare_v2()] call (or its variants) that was used to ** create the statement in the first place. */ -SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); +SQLITE_API sqlite3 *SQLITE_STDCALL sqlite3_db_handle(sqlite3_stmt*); /* ** CAPI3REF: Return The Filename For A Database Connection +** METHOD: sqlite3 ** ** ^The sqlite3_db_filename(D,N) interface returns a pointer to a filename ** associated with database N of connection D. ^The main database file @@ -4913,19 +4992,21 @@ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); ** will be an absolute pathname, even if the filename used ** to open the database originally was a URI or relative pathname. */ -SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName); +SQLITE_API const char *SQLITE_STDCALL sqlite3_db_filename(sqlite3 *db, const char *zDbName); /* ** CAPI3REF: Determine if a database is read-only +** METHOD: sqlite3 ** ** ^The sqlite3_db_readonly(D,N) interface returns 1 if the database N ** of connection D is read-only, 0 if it is read/write, or -1 if N is not ** the name of a database on connection D. */ -SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName); +SQLITE_API int SQLITE_STDCALL sqlite3_db_readonly(sqlite3 *db, const char *zDbName); /* ** CAPI3REF: Find the next prepared statement +** METHOD: sqlite3 ** ** ^This interface returns a pointer to the next [prepared statement] after ** pStmt associated with the [database connection] pDb. ^If pStmt is NULL @@ -4937,10 +5018,11 @@ SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName); ** [sqlite3_next_stmt(D,S)] must refer to an open database ** connection and in particular must not be a NULL pointer. */ -SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); +SQLITE_API sqlite3_stmt *SQLITE_STDCALL sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); /* ** CAPI3REF: Commit And Rollback Notification Callbacks +** METHOD: sqlite3 ** ** ^The sqlite3_commit_hook() interface registers a callback ** function to be invoked whenever a transaction is [COMMIT | committed]. @@ -4985,11 +5067,12 @@ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); ** ** See also the [sqlite3_update_hook()] interface. */ -SQLITE_API void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); -SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); +SQLITE_API void *SQLITE_STDCALL sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); +SQLITE_API void *SQLITE_STDCALL sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); /* ** CAPI3REF: Data Change Notification Callbacks +** METHOD: sqlite3 ** ** ^The sqlite3_update_hook() interface registers a callback function ** with the [database connection] identified by the first argument @@ -5036,7 +5119,7 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); ** See also the [sqlite3_commit_hook()] and [sqlite3_rollback_hook()] ** interfaces. */ -SQLITE_API void *sqlite3_update_hook( +SQLITE_API void *SQLITE_STDCALL sqlite3_update_hook( sqlite3*, void(*)(void *,int ,char const *,char const *,sqlite3_int64), void* @@ -5066,12 +5149,17 @@ SQLITE_API void *sqlite3_update_hook( ** future releases of SQLite. Applications that care about shared ** cache setting should set it explicitly. ** +** Note: This method is disabled on MacOS X 10.7 and iOS version 5.0 +** and will always return SQLITE_MISUSE. On those systems, +** shared cache mode should be enabled per-database connection via +** [sqlite3_open_v2()] with [SQLITE_OPEN_SHAREDCACHE]. +** ** This interface is threadsafe on processors where writing a ** 32-bit integer is atomic. ** ** See Also: [SQLite Shared-Cache Mode] */ -SQLITE_API int sqlite3_enable_shared_cache(int); +SQLITE_API int SQLITE_STDCALL sqlite3_enable_shared_cache(int); /* ** CAPI3REF: Attempt To Free Heap Memory @@ -5087,10 +5175,11 @@ SQLITE_API int sqlite3_enable_shared_cache(int); ** ** See also: [sqlite3_db_release_memory()] */ -SQLITE_API int sqlite3_release_memory(int); +SQLITE_API int SQLITE_STDCALL sqlite3_release_memory(int); /* ** CAPI3REF: Free Memory Used By A Database Connection +** METHOD: sqlite3 ** ** ^The sqlite3_db_release_memory(D) interface attempts to free as much heap ** memory as possible from database connection D. Unlike the @@ -5100,7 +5189,7 @@ SQLITE_API int sqlite3_release_memory(int); ** ** See also: [sqlite3_release_memory()] */ -SQLITE_API int sqlite3_db_release_memory(sqlite3*); +SQLITE_API int SQLITE_STDCALL sqlite3_db_release_memory(sqlite3*); /* ** CAPI3REF: Impose A Limit On Heap Size @@ -5152,7 +5241,7 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*); ** The circumstances under which SQLite will enforce the soft heap limit may ** changes in future releases of SQLite. */ -SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_soft_heap_limit64(sqlite3_int64 N); /* ** CAPI3REF: Deprecated Soft Heap Limit Interface @@ -5163,11 +5252,12 @@ SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N); ** only. All new applications should use the ** [sqlite3_soft_heap_limit64()] interface rather than this one. */ -SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N); +SQLITE_API SQLITE_DEPRECATED void SQLITE_STDCALL sqlite3_soft_heap_limit(int N); /* ** CAPI3REF: Extract Metadata About A Column Of A Table +** METHOD: sqlite3 ** ** ^(The sqlite3_table_column_metadata(X,D,T,C,....) routine returns ** information about column C of table T in database D @@ -5232,7 +5322,7 @@ SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N); ** parsed, if that has not already been done, and returns an error if ** any errors are encountered while loading the schema. */ -SQLITE_API int sqlite3_table_column_metadata( +SQLITE_API int SQLITE_STDCALL sqlite3_table_column_metadata( sqlite3 *db, /* Connection handle */ const char *zDbName, /* Database name or NULL */ const char *zTableName, /* Table name */ @@ -5246,6 +5336,7 @@ SQLITE_API int sqlite3_table_column_metadata( /* ** CAPI3REF: Load An Extension +** METHOD: sqlite3 ** ** ^This interface loads an SQLite extension library from the named file. ** @@ -5278,7 +5369,7 @@ SQLITE_API int sqlite3_table_column_metadata( ** ** See also the [load_extension() SQL function]. */ -SQLITE_API int sqlite3_load_extension( +SQLITE_API int SQLITE_STDCALL sqlite3_load_extension( sqlite3 *db, /* Load the extension into this database connection */ const char *zFile, /* Name of the shared library containing extension */ const char *zProc, /* Entry point. Derived from zFile if 0 */ @@ -5287,6 +5378,7 @@ SQLITE_API int sqlite3_load_extension( /* ** CAPI3REF: Enable Or Disable Extension Loading +** METHOD: sqlite3 ** ** ^So as not to open security holes in older applications that are ** unprepared to deal with [extension loading], and as a means of disabling @@ -5298,7 +5390,7 @@ SQLITE_API int sqlite3_load_extension( ** to turn extension loading on and call it with onoff==0 to turn ** it back off again. */ -SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); +SQLITE_API int SQLITE_STDCALL sqlite3_enable_load_extension(sqlite3 *db, int onoff); /* ** CAPI3REF: Automatically Load Statically Linked Extensions @@ -5336,7 +5428,7 @@ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); ** See also: [sqlite3_reset_auto_extension()] ** and [sqlite3_cancel_auto_extension()] */ -SQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void)); +SQLITE_API int SQLITE_STDCALL sqlite3_auto_extension(void (*xEntryPoint)(void)); /* ** CAPI3REF: Cancel Automatic Extension Loading @@ -5348,7 +5440,7 @@ SQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void)); ** unregistered and it returns 0 if X was not on the list of initialization ** routines. */ -SQLITE_API int sqlite3_cancel_auto_extension(void (*xEntryPoint)(void)); +SQLITE_API int SQLITE_STDCALL sqlite3_cancel_auto_extension(void (*xEntryPoint)(void)); /* ** CAPI3REF: Reset Automatic Extension Loading @@ -5356,7 +5448,7 @@ SQLITE_API int sqlite3_cancel_auto_extension(void (*xEntryPoint)(void)); ** ^This interface disables all automatic extensions previously ** registered using [sqlite3_auto_extension()]. */ -SQLITE_API void sqlite3_reset_auto_extension(void); +SQLITE_API void SQLITE_STDCALL sqlite3_reset_auto_extension(void); /* ** The interface to the virtual-table mechanism is currently considered @@ -5536,6 +5628,7 @@ struct sqlite3_index_info { /* ** CAPI3REF: Register A Virtual Table Implementation +** METHOD: sqlite3 ** ** ^These routines are used to register a new [virtual table module] name. ** ^Module names must be registered before @@ -5559,13 +5652,13 @@ struct sqlite3_index_info { ** interface is equivalent to sqlite3_create_module_v2() with a NULL ** destructor. */ -SQLITE_API int sqlite3_create_module( +SQLITE_API int SQLITE_STDCALL sqlite3_create_module( sqlite3 *db, /* SQLite connection to register module with */ const char *zName, /* Name of the module */ const sqlite3_module *p, /* Methods for the module */ void *pClientData /* Client data for xCreate/xConnect */ ); -SQLITE_API int sqlite3_create_module_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_create_module_v2( sqlite3 *db, /* SQLite connection to register module with */ const char *zName, /* Name of the module */ const sqlite3_module *p, /* Methods for the module */ @@ -5593,7 +5686,7 @@ SQLITE_API int sqlite3_create_module_v2( */ struct sqlite3_vtab { const sqlite3_module *pModule; /* The module for this virtual table */ - int nRef; /* NO LONGER USED */ + int nRef; /* Number of open cursors */ char *zErrMsg; /* Error message from sqlite3_mprintf() */ /* Virtual table implementations will typically add additional fields */ }; @@ -5628,10 +5721,11 @@ struct sqlite3_vtab_cursor { ** to declare the format (the names and datatypes of the columns) of ** the virtual tables they implement. */ -SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL); +SQLITE_API int SQLITE_STDCALL sqlite3_declare_vtab(sqlite3*, const char *zSQL); /* ** CAPI3REF: Overload A Function For A Virtual Table +** METHOD: sqlite3 ** ** ^(Virtual tables can provide alternative implementations of functions ** using the [xFindFunction] method of the [virtual table module]. @@ -5646,7 +5740,7 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL); ** purpose is to be a placeholder function that can be overloaded ** by a [virtual table]. */ -SQLITE_API int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg); +SQLITE_API int SQLITE_STDCALL sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg); /* ** The interface to the virtual-table mechanism defined above (back up @@ -5674,6 +5768,8 @@ typedef struct sqlite3_blob sqlite3_blob; /* ** CAPI3REF: Open A BLOB For Incremental I/O +** METHOD: sqlite3 +** CONSTRUCTOR: sqlite3_blob ** ** ^(This interfaces opens a [BLOB handle | handle] to the BLOB located ** in row iRow, column zColumn, table zTable in database zDb; @@ -5743,7 +5839,7 @@ typedef struct sqlite3_blob sqlite3_blob; ** To avoid a resource leak, every open [BLOB handle] should eventually ** be released by a call to [sqlite3_blob_close()]. */ -SQLITE_API int sqlite3_blob_open( +SQLITE_API int SQLITE_STDCALL sqlite3_blob_open( sqlite3*, const char *zDb, const char *zTable, @@ -5755,6 +5851,7 @@ SQLITE_API int sqlite3_blob_open( /* ** CAPI3REF: Move a BLOB Handle to a New Row +** METHOD: sqlite3_blob ** ** ^This function is used to move an existing blob handle so that it points ** to a different row of the same database table. ^The new row is identified @@ -5775,10 +5872,11 @@ SQLITE_API int sqlite3_blob_open( ** ** ^This function sets the database handle error code and message. */ -SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64); +SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64); /* ** CAPI3REF: Close A BLOB Handle +** DESTRUCTOR: sqlite3_blob ** ** ^This function closes an open [BLOB handle]. ^(The BLOB handle is closed ** unconditionally. Even if this routine returns an error code, the @@ -5797,10 +5895,11 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_i ** is passed a valid open blob handle, the values returned by the ** sqlite3_errcode() and sqlite3_errmsg() functions are set before returning. */ -SQLITE_API int sqlite3_blob_close(sqlite3_blob *); +SQLITE_API int SQLITE_STDCALL sqlite3_blob_close(sqlite3_blob *); /* ** CAPI3REF: Return The Size Of An Open BLOB +** METHOD: sqlite3_blob ** ** ^Returns the size in bytes of the BLOB accessible via the ** successfully opened [BLOB handle] in its only argument. ^The @@ -5812,10 +5911,11 @@ SQLITE_API int sqlite3_blob_close(sqlite3_blob *); ** been closed by [sqlite3_blob_close()]. Passing any other pointer in ** to this routine results in undefined and probably undesirable behavior. */ -SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *); +SQLITE_API int SQLITE_STDCALL sqlite3_blob_bytes(sqlite3_blob *); /* ** CAPI3REF: Read Data From A BLOB Incrementally +** METHOD: sqlite3_blob ** ** ^(This function is used to read data from an open [BLOB handle] into a ** caller-supplied buffer. N bytes of data are copied into buffer Z @@ -5840,10 +5940,11 @@ SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *); ** ** See also: [sqlite3_blob_write()]. */ -SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); +SQLITE_API int SQLITE_STDCALL sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); /* ** CAPI3REF: Write Data Into A BLOB Incrementally +** METHOD: sqlite3_blob ** ** ^(This function is used to write data into an open [BLOB handle] from a ** caller-supplied buffer. N bytes of data are copied from the buffer Z @@ -5881,7 +5982,7 @@ SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); ** ** See also: [sqlite3_blob_read()]. */ -SQLITE_API int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); +SQLITE_API int SQLITE_STDCALL sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); /* ** CAPI3REF: Virtual File System Objects @@ -5912,9 +6013,9 @@ SQLITE_API int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOff ** ^(If the default VFS is unregistered, another VFS is chosen as ** the default. The choice for the new VFS is arbitrary.)^ */ -SQLITE_API sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName); -SQLITE_API int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt); -SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); +SQLITE_API sqlite3_vfs *SQLITE_STDCALL sqlite3_vfs_find(const char *zVfsName); +SQLITE_API int SQLITE_STDCALL sqlite3_vfs_register(sqlite3_vfs*, int makeDflt); +SQLITE_API int SQLITE_STDCALL sqlite3_vfs_unregister(sqlite3_vfs*); /* ** CAPI3REF: Mutexes @@ -6027,11 +6128,11 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); ** ** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. */ -SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int); -SQLITE_API void sqlite3_mutex_free(sqlite3_mutex*); -SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex*); -SQLITE_API int sqlite3_mutex_try(sqlite3_mutex*); -SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex*); +SQLITE_API sqlite3_mutex *SQLITE_STDCALL sqlite3_mutex_alloc(int); +SQLITE_API void SQLITE_STDCALL sqlite3_mutex_free(sqlite3_mutex*); +SQLITE_API void SQLITE_STDCALL sqlite3_mutex_enter(sqlite3_mutex*); +SQLITE_API int SQLITE_STDCALL sqlite3_mutex_try(sqlite3_mutex*); +SQLITE_API void SQLITE_STDCALL sqlite3_mutex_leave(sqlite3_mutex*); /* ** CAPI3REF: Mutex Methods Object @@ -6141,8 +6242,8 @@ struct sqlite3_mutex_methods { ** interface should also return 1 when given a NULL pointer. */ #ifndef NDEBUG -SQLITE_API int sqlite3_mutex_held(sqlite3_mutex*); -SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); +SQLITE_API int SQLITE_STDCALL sqlite3_mutex_held(sqlite3_mutex*); +SQLITE_API int SQLITE_STDCALL sqlite3_mutex_notheld(sqlite3_mutex*); #endif /* @@ -6171,6 +6272,7 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); /* ** CAPI3REF: Retrieve the mutex for a database connection +** METHOD: sqlite3 ** ** ^This interface returns a pointer the [sqlite3_mutex] object that ** serializes access to the [database connection] given in the argument @@ -6178,10 +6280,11 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); ** ^If the [threading mode] is Single-thread or Multi-thread then this ** routine returns a NULL pointer. */ -SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*); +SQLITE_API sqlite3_mutex *SQLITE_STDCALL sqlite3_db_mutex(sqlite3*); /* ** CAPI3REF: Low-Level Control Of Database Files +** METHOD: sqlite3 ** ** ^The [sqlite3_file_control()] interface makes a direct call to the ** xFileControl method for the [sqlite3_io_methods] object associated @@ -6212,7 +6315,7 @@ SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*); ** ** See also: [SQLITE_FCNTL_LOCKSTATE] */ -SQLITE_API int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*); +SQLITE_API int SQLITE_STDCALL sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*); /* ** CAPI3REF: Testing Interface @@ -6231,7 +6334,7 @@ SQLITE_API int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void* ** Unlike most of the SQLite API, this function is not guaranteed to ** operate consistently from one release to the next. */ -SQLITE_API int sqlite3_test_control(int op, ...); +SQLITE_API int SQLITE_CDECL sqlite3_test_control(int op, ...); /* ** CAPI3REF: Testing Interface Operation Codes @@ -6265,12 +6368,13 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_BYTEORDER 22 #define SQLITE_TESTCTRL_ISINIT 23 #define SQLITE_TESTCTRL_SORTER_MMAP 24 -#define SQLITE_TESTCTRL_LAST 24 +#define SQLITE_TESTCTRL_IMPOSTER 25 +#define SQLITE_TESTCTRL_LAST 25 /* ** CAPI3REF: SQLite Runtime Status ** -** ^This interface is used to retrieve runtime status information +** ^These interfaces are used to retrieve runtime status information ** about the performance of SQLite, and optionally to reset various ** highwater marks. ^The first argument is an integer code for ** the specific parameter to measure. ^(Recognized integer codes @@ -6284,19 +6388,22 @@ SQLITE_API int sqlite3_test_control(int op, ...); ** ^(Other parameters record only the highwater mark and not the current ** value. For these latter parameters nothing is written into *pCurrent.)^ ** -** ^The sqlite3_status() routine returns SQLITE_OK on success and a -** non-zero [error code] on failure. +** ^The sqlite3_status() and sqlite3_status64() routines return +** SQLITE_OK on success and a non-zero [error code] on failure. ** -** This routine is threadsafe but is not atomic. This routine can be -** called while other threads are running the same or different SQLite -** interfaces. However the values returned in *pCurrent and -** *pHighwater reflect the status of SQLite at different points in time -** and it is possible that another thread might change the parameter -** in between the times when *pCurrent and *pHighwater are written. +** If either the current value or the highwater mark is too large to +** be represented by a 32-bit integer, then the values returned by +** sqlite3_status() are undefined. ** ** See also: [sqlite3_db_status()] */ -SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag); +SQLITE_API int SQLITE_STDCALL sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag); +SQLITE_API int SQLITE_STDCALL sqlite3_status64( + int op, + sqlite3_int64 *pCurrent, + sqlite3_int64 *pHighwater, + int resetFlag +); /* @@ -6394,6 +6501,7 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF /* ** CAPI3REF: Database Connection Status +** METHOD: sqlite3 ** ** ^This interface is used to retrieve runtime status information ** about a single [database connection]. ^The first argument is the @@ -6414,7 +6522,7 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF ** ** See also: [sqlite3_status()] and [sqlite3_stmt_status()]. */ -SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); +SQLITE_API int SQLITE_STDCALL sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); /* ** CAPI3REF: Status Parameters for database connections @@ -6522,6 +6630,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r /* ** CAPI3REF: Prepared Statement Status +** METHOD: sqlite3_stmt ** ** ^(Each prepared statement maintains various ** [SQLITE_STMTSTATUS counters] that measure the number @@ -6543,7 +6652,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r ** ** See also: [sqlite3_status()] and [sqlite3_db_status()]. */ -SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); +SQLITE_API int SQLITE_STDCALL sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); /* ** CAPI3REF: Status Parameters for prepared statements @@ -6966,20 +7075,20 @@ typedef struct sqlite3_backup sqlite3_backup; ** is not a permanent error and does not affect the return value of ** sqlite3_backup_finish(). ** -** [[sqlite3_backup__remaining()]] [[sqlite3_backup_pagecount()]] +** [[sqlite3_backup_remaining()]] [[sqlite3_backup_pagecount()]] ** sqlite3_backup_remaining() and sqlite3_backup_pagecount() ** -** ^Each call to sqlite3_backup_step() sets two values inside -** the [sqlite3_backup] object: the number of pages still to be backed -** up and the total number of pages in the source database file. -** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces -** retrieve these two values, respectively. -** -** ^The values returned by these functions are only updated by -** sqlite3_backup_step(). ^If the source database is modified during a backup -** operation, then the values are not updated to account for any extra -** pages that need to be updated or the size of the source database file -** changing. +** ^The sqlite3_backup_remaining() routine returns the number of pages still +** to be backed up at the conclusion of the most recent sqlite3_backup_step(). +** ^The sqlite3_backup_pagecount() routine returns the total number of pages +** in the source database at the conclusion of the most recent +** sqlite3_backup_step(). +** ^(The values returned by these functions are only updated by +** sqlite3_backup_step(). If the source database is modified in a way that +** changes the size of the source database or the number of pages remaining, +** those changes are not reflected in the output of sqlite3_backup_pagecount() +** and sqlite3_backup_remaining() until after the next +** sqlite3_backup_step().)^ ** ** Concurrent Usage of Database Handles ** @@ -7012,19 +7121,20 @@ typedef struct sqlite3_backup sqlite3_backup; ** same time as another thread is invoking sqlite3_backup_step() it is ** possible that they return invalid values. */ -SQLITE_API sqlite3_backup *sqlite3_backup_init( +SQLITE_API sqlite3_backup *SQLITE_STDCALL sqlite3_backup_init( sqlite3 *pDest, /* Destination database handle */ const char *zDestName, /* Destination database name */ sqlite3 *pSource, /* Source database handle */ const char *zSourceName /* Source database name */ ); -SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage); -SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p); -SQLITE_API int sqlite3_backup_remaining(sqlite3_backup *p); -SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); +SQLITE_API int SQLITE_STDCALL sqlite3_backup_step(sqlite3_backup *p, int nPage); +SQLITE_API int SQLITE_STDCALL sqlite3_backup_finish(sqlite3_backup *p); +SQLITE_API int SQLITE_STDCALL sqlite3_backup_remaining(sqlite3_backup *p); +SQLITE_API int SQLITE_STDCALL sqlite3_backup_pagecount(sqlite3_backup *p); /* ** CAPI3REF: Unlock Notification +** METHOD: sqlite3 ** ** ^When running in shared-cache mode, a database operation may fail with ** an [SQLITE_LOCKED] error if the required locks on the shared-cache or @@ -7137,7 +7247,7 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); ** the special "DROP TABLE/INDEX" case, the extended error code is just ** SQLITE_LOCKED.)^ */ -SQLITE_API int sqlite3_unlock_notify( +SQLITE_API int SQLITE_STDCALL sqlite3_unlock_notify( sqlite3 *pBlocked, /* Waiting connection */ void (*xNotify)(void **apArg, int nArg), /* Callback function to invoke */ void *pNotifyArg /* Argument to pass to xNotify */ @@ -7152,8 +7262,8 @@ SQLITE_API int sqlite3_unlock_notify( ** strings in a case-independent fashion, using the same definition of "case ** independence" that SQLite uses internally when comparing identifiers. */ -SQLITE_API int sqlite3_stricmp(const char *, const char *); -SQLITE_API int sqlite3_strnicmp(const char *, const char *, int); +SQLITE_API int SQLITE_STDCALL sqlite3_stricmp(const char *, const char *); +SQLITE_API int SQLITE_STDCALL sqlite3_strnicmp(const char *, const char *, int); /* ** CAPI3REF: String Globbing @@ -7168,7 +7278,7 @@ SQLITE_API int sqlite3_strnicmp(const char *, const char *, int); ** Note that this routine returns zero on a match and non-zero if the strings ** do not match, the same as [sqlite3_stricmp()] and [sqlite3_strnicmp()]. */ -SQLITE_API int sqlite3_strglob(const char *zGlob, const char *zStr); +SQLITE_API int SQLITE_STDCALL sqlite3_strglob(const char *zGlob, const char *zStr); /* ** CAPI3REF: Error Logging Interface @@ -7191,10 +7301,11 @@ SQLITE_API int sqlite3_strglob(const char *zGlob, const char *zStr); ** a few hundred characters, it will be truncated to the length of the ** buffer. */ -SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...); +SQLITE_API void SQLITE_CDECL sqlite3_log(int iErrCode, const char *zFormat, ...); /* ** CAPI3REF: Write-Ahead Log Commit Hook +** METHOD: sqlite3 ** ** ^The [sqlite3_wal_hook()] function is used to register a callback that ** is invoked each time data is committed to a database in wal mode. @@ -7226,7 +7337,7 @@ SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...); ** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will ** those overwrite any prior [sqlite3_wal_hook()] settings. */ -SQLITE_API void *sqlite3_wal_hook( +SQLITE_API void *SQLITE_STDCALL sqlite3_wal_hook( sqlite3*, int(*)(void *,sqlite3*,const char*,int), void* @@ -7234,6 +7345,7 @@ SQLITE_API void *sqlite3_wal_hook( /* ** CAPI3REF: Configure an auto-checkpoint +** METHOD: sqlite3 ** ** ^The [sqlite3_wal_autocheckpoint(D,N)] is a wrapper around ** [sqlite3_wal_hook()] that causes any database on [database connection] D @@ -7260,10 +7372,11 @@ SQLITE_API void *sqlite3_wal_hook( ** is only necessary if the default setting is found to be suboptimal ** for a particular application. */ -SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N); +SQLITE_API int SQLITE_STDCALL sqlite3_wal_autocheckpoint(sqlite3 *db, int N); /* ** CAPI3REF: Checkpoint a database +** METHOD: sqlite3 ** ** ^(The sqlite3_wal_checkpoint(D,X) is equivalent to ** [sqlite3_wal_checkpoint_v2](D,X,[SQLITE_CHECKPOINT_PASSIVE],0,0).)^ @@ -7281,10 +7394,11 @@ SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N); ** start a callback but which do not need the full power (and corresponding ** complication) of [sqlite3_wal_checkpoint_v2()]. */ -SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); +SQLITE_API int SQLITE_STDCALL sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); /* ** CAPI3REF: Checkpoint a database +** METHOD: sqlite3 ** ** ^(The sqlite3_wal_checkpoint_v2(D,X,M,L,C) interface runs a checkpoint ** operation on database X of [database connection] D in mode M. Status @@ -7374,7 +7488,7 @@ SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); ** ^The [PRAGMA wal_checkpoint] command can be used to invoke this interface ** from SQL. */ -SQLITE_API int sqlite3_wal_checkpoint_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_wal_checkpoint_v2( sqlite3 *db, /* Database handle */ const char *zDb, /* Name of attached database (or NULL) */ int eMode, /* SQLITE_CHECKPOINT_* value */ @@ -7410,7 +7524,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2( ** this function. (See [SQLITE_VTAB_CONSTRAINT_SUPPORT].) Further options ** may be added in the future. */ -SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); +SQLITE_API int SQLITE_CDECL sqlite3_vtab_config(sqlite3*, int op, ...); /* ** CAPI3REF: Virtual Table Configuration Options @@ -7463,7 +7577,7 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); ** of the SQL statement that triggered the call to the [xUpdate] method of the ** [virtual table]. */ -SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); +SQLITE_API int SQLITE_STDCALL sqlite3_vtab_on_conflict(sqlite3 *); /* ** CAPI3REF: Conflict resolution modes @@ -7539,6 +7653,7 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); /* ** CAPI3REF: Prepared Statement Scan Status +** METHOD: sqlite3_stmt ** ** This interface returns information about the predicted and measured ** performance for pStmt. Advanced applications can use this @@ -7567,7 +7682,7 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); ** ** See also: [sqlite3_stmt_scanstatus_reset()] */ -SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_stmt_scanstatus( +SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_stmt_scanstatus( sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ int idx, /* Index of loop to report on */ int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ @@ -7576,13 +7691,14 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_stmt_scanstatus( /* ** CAPI3REF: Zero Scan-Status Counters +** METHOD: sqlite3_stmt ** ** ^Zero all [sqlite3_stmt_scanstatus()] related event counters. ** ** This API is only available if the library is built with pre-processor ** symbol [SQLITE_ENABLE_STMT_SCANSTATUS] defined. */ -SQLITE_API SQLITE_EXPERIMENTAL void sqlite3_stmt_scanstatus_reset(sqlite3_stmt*); +SQLITE_API SQLITE_EXPERIMENTAL void SQLITE_STDCALL sqlite3_stmt_scanstatus_reset(sqlite3_stmt*); /* @@ -7637,7 +7753,7 @@ typedef struct sqlite3_rtree_query_info sqlite3_rtree_query_info; ** ** SELECT ... FROM WHERE MATCH $zGeom(... params ...) */ -SQLITE_API int sqlite3_rtree_geometry_callback( +SQLITE_API int SQLITE_STDCALL sqlite3_rtree_geometry_callback( sqlite3 *db, const char *zGeom, int (*xGeom)(sqlite3_rtree_geometry*, int, sqlite3_rtree_dbl*,int*), @@ -7663,7 +7779,7 @@ struct sqlite3_rtree_geometry { ** ** SELECT ... FROM WHERE MATCH $zQueryFunc(... params ...) */ -SQLITE_API int sqlite3_rtree_query_callback( +SQLITE_API int SQLITE_STDCALL sqlite3_rtree_query_callback( sqlite3 *db, const char *zQueryFunc, int (*xQueryFunc)(sqlite3_rtree_query_info*), diff --git a/TMessagesProj/libs/armeabi-v7a/libtmessages.7.so b/TMessagesProj/libs/armeabi-v7a/libtmessages.8.so similarity index 65% rename from TMessagesProj/libs/armeabi-v7a/libtmessages.7.so rename to TMessagesProj/libs/armeabi-v7a/libtmessages.8.so index c086f4a98..b73f401f3 100755 Binary files a/TMessagesProj/libs/armeabi-v7a/libtmessages.7.so and b/TMessagesProj/libs/armeabi-v7a/libtmessages.8.so differ diff --git a/TMessagesProj/libs/armeabi/libtmessages.7.so b/TMessagesProj/libs/armeabi/libtmessages.8.so similarity index 57% rename from TMessagesProj/libs/armeabi/libtmessages.7.so rename to TMessagesProj/libs/armeabi/libtmessages.8.so index 42ad265ae..b720a57b4 100755 Binary files a/TMessagesProj/libs/armeabi/libtmessages.7.so and b/TMessagesProj/libs/armeabi/libtmessages.8.so differ diff --git a/TMessagesProj/libs/x86/libtmessages.7.so b/TMessagesProj/libs/x86/libtmessages.8.so similarity index 56% rename from TMessagesProj/libs/x86/libtmessages.7.so rename to TMessagesProj/libs/x86/libtmessages.8.so index aaa768eac..0b3e8df01 100755 Binary files a/TMessagesProj/libs/x86/libtmessages.7.so and b/TMessagesProj/libs/x86/libtmessages.8.so differ diff --git a/TMessagesProj/src/main/AndroidManifest.xml b/TMessagesProj/src/main/AndroidManifest.xml index dd3c5fc25..2009cf46a 100644 --- a/TMessagesProj/src/main/AndroidManifest.xml +++ b/TMessagesProj/src/main/AndroidManifest.xml @@ -123,6 +123,18 @@ android:windowSoftInputMode="adjustResize|stateHidden"> + + + + + + + + + + + + @@ -168,6 +180,8 @@ + + diff --git a/TMessagesProj/src/main/assets/emoji/emoji2.0x_0.jpg b/TMessagesProj/src/main/assets/emoji/emoji2.0x_0.jpg deleted file mode 100644 index 1cf58511e..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji2.0x_0.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji2.0x_1.jpg b/TMessagesProj/src/main/assets/emoji/emoji2.0x_1.jpg deleted file mode 100644 index f5a3bd8a3..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji2.0x_1.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji2.0x_2.jpg b/TMessagesProj/src/main/assets/emoji/emoji2.0x_2.jpg deleted file mode 100644 index d0db3e171..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji2.0x_2.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji2.0x_3.jpg b/TMessagesProj/src/main/assets/emoji/emoji2.0x_3.jpg deleted file mode 100644 index 225f2b8c5..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji2.0x_3.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji2.0x_4.jpg b/TMessagesProj/src/main/assets/emoji/emoji2.0x_4.jpg deleted file mode 100644 index 453875c0d..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji2.0x_4.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_0.jpg b/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_0.jpg deleted file mode 100644 index 53416f240..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_0.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_1.jpg b/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_1.jpg deleted file mode 100644 index 8b7a562ea..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_1.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_2.jpg b/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_2.jpg deleted file mode 100644 index 48424203e..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_2.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_3.jpg b/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_3.jpg deleted file mode 100644 index 98cef882c..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_3.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_4.jpg b/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_4.jpg deleted file mode 100644 index 0f8980a81..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji2.0x_a_4.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji3.0x_0.jpg b/TMessagesProj/src/main/assets/emoji/emoji3.0x_0.jpg deleted file mode 100644 index e09c30c59..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji3.0x_0.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji3.0x_1.jpg b/TMessagesProj/src/main/assets/emoji/emoji3.0x_1.jpg deleted file mode 100644 index e0b0752ed..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji3.0x_1.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji3.0x_2.jpg b/TMessagesProj/src/main/assets/emoji/emoji3.0x_2.jpg deleted file mode 100644 index ffd3c8480..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji3.0x_2.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji3.0x_3.jpg b/TMessagesProj/src/main/assets/emoji/emoji3.0x_3.jpg deleted file mode 100644 index dd229d2a5..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji3.0x_3.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji3.0x_4.jpg b/TMessagesProj/src/main/assets/emoji/emoji3.0x_4.jpg deleted file mode 100644 index 9127912ff..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji3.0x_4.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_0.jpg b/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_0.jpg deleted file mode 100644 index e0e9339ec..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_0.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_1.jpg b/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_1.jpg deleted file mode 100644 index f5e831aec..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_1.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_2.jpg b/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_2.jpg deleted file mode 100644 index d04659d6a..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_2.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_3.jpg b/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_3.jpg deleted file mode 100644 index 1a3372391..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_3.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_4.jpg b/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_4.jpg deleted file mode 100644 index 7ce611f72..000000000 Binary files a/TMessagesProj/src/main/assets/emoji/emoji3.0x_a_4.jpg and /dev/null differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_0.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_0.jpg new file mode 100644 index 000000000..251747ff3 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_0.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_1.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_1.jpg new file mode 100644 index 000000000..21bc015ea Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_1.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_2.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_2.jpg new file mode 100644 index 000000000..c1c6fd3b7 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_2.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_3.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_3.jpg new file mode 100644 index 000000000..bb398488c Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_3.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_4.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_4.jpg new file mode 100644 index 000000000..a74e170f9 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_4.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_0.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_0.jpg new file mode 100644 index 000000000..2fe760d04 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_0.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_1.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_1.jpg new file mode 100644 index 000000000..776bf5887 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_1.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_2.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_2.jpg new file mode 100644 index 000000000..43a7aa378 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_2.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_3.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_3.jpg new file mode 100644 index 000000000..95d146fea Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_3.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_4.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_4.jpg new file mode 100644 index 000000000..2be40c476 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji2.0x_a_4.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_0.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_0.jpg new file mode 100644 index 000000000..bc1d03b45 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_0.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_1.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_1.jpg new file mode 100644 index 000000000..8288773c4 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_1.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_2.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_2.jpg new file mode 100644 index 000000000..feea12096 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_2.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_3.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_3.jpg new file mode 100644 index 000000000..0e3bcede0 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_3.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_4.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_4.jpg new file mode 100644 index 000000000..1f74e8359 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_4.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_0.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_0.jpg new file mode 100644 index 000000000..5218c3ca4 Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_0.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_1.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_1.jpg new file mode 100644 index 000000000..e6192101a Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_1.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_2.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_2.jpg new file mode 100644 index 000000000..58476054b Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_2.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_3.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_3.jpg new file mode 100644 index 000000000..7e4ea184c Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_3.jpg differ diff --git a/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_4.jpg b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_4.jpg new file mode 100644 index 000000000..9bb3ea6cd Binary files /dev/null and b/TMessagesProj/src/main/assets/emoji/v4_emoji3.0x_a_4.jpg differ diff --git a/TMessagesProj/src/main/java/org/telegram/PhoneFormat/CallingCodeInfo.java b/TMessagesProj/src/main/java/org/telegram/PhoneFormat/CallingCodeInfo.java index 666d508a0..33a53fbf9 100644 --- a/TMessagesProj/src/main/java/org/telegram/PhoneFormat/CallingCodeInfo.java +++ b/TMessagesProj/src/main/java/org/telegram/PhoneFormat/CallingCodeInfo.java @@ -27,11 +27,11 @@ package org.telegram.PhoneFormat; import java.util.ArrayList; public class CallingCodeInfo { - public ArrayList countries = new ArrayList(); + public ArrayList countries = new ArrayList<>(); public String callingCode = ""; - public ArrayList trunkPrefixes = new ArrayList(); - public ArrayList intlPrefixes = new ArrayList(); - public ArrayList ruleSets = new ArrayList(); + public ArrayList trunkPrefixes = new ArrayList<>(); + public ArrayList intlPrefixes = new ArrayList<>(); + public ArrayList ruleSets = new ArrayList<>(); //public ArrayList formatStrings; String matchingAccessCode(String str) { @@ -107,14 +107,14 @@ public class CallingCodeInfo { for (RuleSet set : ruleSets) { boolean valid = set.isValid(str, intlPrefix, trunkPrefix, true); if (valid) { - return valid; + return true; } } for (RuleSet set : ruleSets) { boolean valid = set.isValid(str, intlPrefix, trunkPrefix, false); if (valid) { - return valid; + return true; } } diff --git a/TMessagesProj/src/main/java/org/telegram/android/AndroidUtilities.java b/TMessagesProj/src/main/java/org/telegram/android/AndroidUtilities.java index b14c401b2..17acc3426 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/AndroidUtilities.java +++ b/TMessagesProj/src/main/java/org/telegram/android/AndroidUtilities.java @@ -8,19 +8,26 @@ package org.telegram.android; +import android.annotation.SuppressLint; import android.app.Activity; import android.app.AlertDialog; +import android.content.ContentUris; import android.content.Context; import android.content.DialogInterface; +import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.res.Configuration; +import android.database.Cursor; import android.graphics.Color; import android.graphics.Point; import android.graphics.Rect; import android.graphics.Typeface; import android.graphics.drawable.Drawable; +import android.net.Uri; import android.os.Build; import android.os.Environment; +import android.provider.DocumentsContract; +import android.provider.MediaStore; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.Spanned; @@ -39,25 +46,39 @@ import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; +import net.hockeyapp.android.CrashManager; +import net.hockeyapp.android.CrashManagerListener; +import net.hockeyapp.android.UpdateManager; + +import org.telegram.messenger.BuildVars; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.FileLog; import org.telegram.messenger.R; import org.telegram.messenger.TLRPC; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.UserConfig; -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 org.telegram.android.AnimationCompat.AnimatorListenerAdapterProxy; +import org.telegram.android.AnimationCompat.AnimatorSetProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.ViewProxy; import org.telegram.ui.Components.ForegroundDetector; import org.telegram.ui.Components.NumberPicker; import org.telegram.ui.Components.TypefaceSpan; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.Hashtable; +import java.util.Locale; public class AndroidUtilities { @@ -72,6 +93,7 @@ public class AndroidUtilities { public static Integer photoSize = null; public static DisplayMetrics displayMetrics = new DisplayMetrics(); public static int leftBaseline; + public static boolean usingHardwareInput; private static Boolean isTablet = null; static { @@ -158,7 +180,7 @@ public class AndroidUtilities { } public static boolean isWaitingForSms() { - boolean value = false; + boolean value; synchronized (smsLock) { value = waitingForSms; } @@ -227,21 +249,38 @@ public class AndroidUtilities { } public static int dp(float value) { + if (value == 0) { + return 0; + } return (int)Math.ceil(density * value); } + public static int compare(int lhs, int rhs) { + if (lhs == rhs) { + return 0; + } else if (lhs > rhs) { + return 1; + } + return -1; + } + public static float dpf2(float value) { + if (value == 0) { + return 0; + } return density * value; } public static void checkDisplaySize() { try { - WindowManager manager = (WindowManager)ApplicationLoader.applicationContext.getSystemService(Context.WINDOW_SERVICE); + Configuration configuration = ApplicationLoader.applicationContext.getResources().getConfiguration(); + usingHardwareInput = configuration.keyboard != Configuration.KEYBOARD_NOKEYS && configuration.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO; + WindowManager manager = (WindowManager) ApplicationLoader.applicationContext.getSystemService(Context.WINDOW_SERVICE); if (manager != null) { Display display = manager.getDefaultDisplay(); if (display != null) { display.getMetrics(displayMetrics); - if(android.os.Build.VERSION.SDK_INT < 13) { + if (android.os.Build.VERSION.SDK_INT < 13) { displaySize.set(display.getWidth(), display.getHeight()); } else { display.getSize(displaySize); @@ -252,6 +291,38 @@ public class AndroidUtilities { } catch (Exception e) { FileLog.e("tmessages", e); } + + /* + keyboardHidden + public static final int KEYBOARDHIDDEN_NO = 1 + Constant for keyboardHidden, value corresponding to the keysexposed resource qualifier. + + public static final int KEYBOARDHIDDEN_UNDEFINED = 0 + Constant for keyboardHidden: a value indicating that no value has been set. + + public static final int KEYBOARDHIDDEN_YES = 2 + Constant for keyboardHidden, value corresponding to the keyshidden resource qualifier. + + hardKeyboardHidden + public static final int HARDKEYBOARDHIDDEN_NO = 1 + Constant for hardKeyboardHidden, value corresponding to the physical keyboard being exposed. + + public static final int HARDKEYBOARDHIDDEN_UNDEFINED = 0 + Constant for hardKeyboardHidden: a value indicating that no value has been set. + + public static final int HARDKEYBOARDHIDDEN_YES = 2 + Constant for hardKeyboardHidden, value corresponding to the physical keyboard being hidden. + + keyboard + public static final int KEYBOARD_12KEY = 3 + Constant for keyboard, value corresponding to the 12key resource qualifier. + + public static final int KEYBOARD_NOKEYS = 1 + Constant for keyboard, value corresponding to the nokeys resource qualifier. + + public static final int KEYBOARD_QWERTY = 2 + Constant for keyboard, value corresponding to the qwerty resource qualifier. + */ } public static float getPixelsInCM(float cm, boolean isX) { @@ -525,11 +596,12 @@ public class AndroidUtilities { } } + @SuppressLint("NewApi") public static void clearDrawableAnimation(View view) { if (Build.VERSION.SDK_INT < 21 || view == null) { return; } - Drawable drawable = null; + Drawable drawable; if (view instanceof ListView) { drawable = ((ListView) view).getSelector(); if (drawable != null) { @@ -546,9 +618,9 @@ public class AndroidUtilities { public static Spannable replaceTags(String str) { try { - int start = -1; + int start; int startColor = -1; - int end = -1; + int end; StringBuilder stringBuilder = new StringBuilder(str); while ((start = stringBuilder.indexOf("
")) != -1) { stringBuilder.replace(start, start + 4, "\n"); @@ -558,10 +630,13 @@ public class AndroidUtilities { } ArrayList bolds = new ArrayList<>(); ArrayList colors = new ArrayList<>(); - while ((start = stringBuilder.indexOf("")) != -1 || (startColor = stringBuilder.indexOf("")) != -1 || (startColor = stringBuilder.indexOf(""); + if (end == -1) { + end = stringBuilder.indexOf(""); + } stringBuilder.replace(end, end + 4, ""); bolds.add(start); bolds.add(end); @@ -575,6 +650,7 @@ public class AndroidUtilities { colors.add(startColor); colors.add(end); colors.add(color); + startColor = -1; } } SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(stringBuilder); @@ -673,4 +749,290 @@ public class AndroidUtilities { window.clearFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); } }*/ + + public static void checkForCrashes(Activity context) { + CrashManager.register(context, BuildVars.HOCKEY_APP_HASH, new CrashManagerListener() { + @Override + public boolean includeDeviceData() { + return true; + } + }); + } + + public static void checkForUpdates(Activity context) { + if (BuildVars.DEBUG_VERSION) { + UpdateManager.register(context, BuildVars.HOCKEY_APP_HASH); + } + } + + public static void unregisterUpdates() { + if (BuildVars.DEBUG_VERSION) { + UpdateManager.unregister(); + } + } + + public static void addMediaToGallery(String fromPath) { + if (fromPath == null) { + return; + } + File f = new File(fromPath); + Uri contentUri = Uri.fromFile(f); + addMediaToGallery(contentUri); + } + + public static void addMediaToGallery(Uri uri) { + if (uri == null) { + return; + } + Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); + mediaScanIntent.setData(uri); + ApplicationLoader.applicationContext.sendBroadcast(mediaScanIntent); + } + + private static File getAlbumDir() { + File storageDir = null; + if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { + storageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "Telegram"); + if (!storageDir.mkdirs()) { + if (!storageDir.exists()){ + FileLog.d("tmessages", "failed to create directory"); + return null; + } + } + } else { + FileLog.d("tmessages", "External storage is not mounted READ/WRITE."); + } + + return storageDir; + } + + @SuppressLint("NewApi") + public static String getPath(final Uri uri) { + try { + final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; + if (isKitKat && DocumentsContract.isDocumentUri(ApplicationLoader.applicationContext, uri)) { + if (isExternalStorageDocument(uri)) { + final String docId = DocumentsContract.getDocumentId(uri); + final String[] split = docId.split(":"); + final String type = split[0]; + if ("primary".equalsIgnoreCase(type)) { + return Environment.getExternalStorageDirectory() + "/" + split[1]; + } + } else if (isDownloadsDocument(uri)) { + final String id = DocumentsContract.getDocumentId(uri); + final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); + return getDataColumn(ApplicationLoader.applicationContext, contentUri, null, null); + } else if (isMediaDocument(uri)) { + final String docId = DocumentsContract.getDocumentId(uri); + final String[] split = docId.split(":"); + final String type = split[0]; + + Uri contentUri = null; + switch (type) { + case "image": + contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + break; + case "video": + contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; + break; + case "audio": + contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; + break; + } + + final String selection = "_id=?"; + final String[] selectionArgs = new String[] { + split[1] + }; + + return getDataColumn(ApplicationLoader.applicationContext, contentUri, selection, selectionArgs); + } + } else if ("content".equalsIgnoreCase(uri.getScheme())) { + return getDataColumn(ApplicationLoader.applicationContext, uri, null, null); + } else if ("file".equalsIgnoreCase(uri.getScheme())) { + return uri.getPath(); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + return null; + } + + public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { + + Cursor cursor = null; + final String column = "_data"; + final String[] projection = { + column + }; + + try { + cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); + if (cursor != null && cursor.moveToFirst()) { + final int column_index = cursor.getColumnIndexOrThrow(column); + return cursor.getString(column_index); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } finally { + if (cursor != null) { + cursor.close(); + } + } + return null; + } + + public static boolean isExternalStorageDocument(Uri uri) { + return "com.android.externalstorage.documents".equals(uri.getAuthority()); + } + + public static boolean isDownloadsDocument(Uri uri) { + return "com.android.providers.downloads.documents".equals(uri.getAuthority()); + } + + public static boolean isMediaDocument(Uri uri) { + return "com.android.providers.media.documents".equals(uri.getAuthority()); + } + + public static File generatePicturePath() { + try { + File storageDir = getAlbumDir(); + String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date()); + return new File(storageDir, "IMG_" + timeStamp + ".jpg"); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + return null; + } + + public static CharSequence generateSearchName(String name, String name2, String q) { + if (name == null && name2 == null) { + return ""; + } + SpannableStringBuilder builder = new SpannableStringBuilder(); + String wholeString = name; + if (wholeString == null || wholeString.length() == 0) { + wholeString = name2; + } else if (name2 != null && name2.length() != 0) { + wholeString += " " + name2; + } + wholeString = wholeString.trim(); + String lower = " " + wholeString.toLowerCase(); + + int index; + int lastIndex = 0; + while ((index = lower.indexOf(" " + q, lastIndex)) != -1) { + int idx = index - (index == 0 ? 0 : 1); + int end = q.length() + (index == 0 ? 0 : 1) + idx; + + if (lastIndex != 0 && lastIndex != idx + 1) { + builder.append(wholeString.substring(lastIndex, idx)); + } else if (lastIndex == 0 && idx != 0) { + builder.append(wholeString.substring(0, idx)); + } + + String query = wholeString.substring(idx, end); + if (query.startsWith(" ")) { + builder.append(" "); + } + query = query.trim(); + builder.append(AndroidUtilities.replaceTags("" + query + "")); + + lastIndex = end; + } + + if (lastIndex != -1 && lastIndex != wholeString.length()) { + builder.append(wholeString.substring(lastIndex, wholeString.length())); + } + + return builder; + } + + public static File generateVideoPath() { + try { + File storageDir = getAlbumDir(); + String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date()); + return new File(storageDir, "VID_" + timeStamp + ".mp4"); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + return null; + } + + public static String formatFileSize(long size) { + if (size < 1024) { + return String.format("%d B", size); + } else if (size < 1024 * 1024) { + return String.format("%.1f KB", size / 1024.0f); + } else if (size < 1024 * 1024 * 1024) { + return String.format("%.1f MB", size / 1024.0f / 1024.0f); + } else { + return String.format("%.1f GB", size / 1024.0f / 1024.0f / 1024.0f); + } + } + + public static byte[] decodeQuotedPrintable(final byte[] bytes) { + if (bytes == null) { + return null; + } + final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + for (int i = 0; i < bytes.length; i++) { + final int b = bytes[i]; + if (b == '=') { + try { + final int u = Character.digit((char) bytes[++i], 16); + final int l = Character.digit((char) bytes[++i], 16); + buffer.write((char) ((u << 4) + l)); + } catch (Exception e) { + FileLog.e("tmessages", e); + return null; + } + } else { + buffer.write(b); + } + } + byte[] array = buffer.toByteArray(); + try { + buffer.close(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + return array; + } + + public static boolean copyFile(InputStream sourceFile, File destFile) throws IOException { + OutputStream out = new FileOutputStream(destFile); + byte[] buf = new byte[4096]; + int len; + while ((len = sourceFile.read(buf)) > 0) { + Thread.yield(); + out.write(buf, 0, len); + } + out.close(); + return true; + } + + public static boolean copyFile(File sourceFile, File destFile) throws IOException { + if (!destFile.exists()) { + destFile.createNewFile(); + } + FileInputStream source = null; + FileOutputStream destination = null; + try { + source = new FileInputStream(sourceFile); + destination = new FileOutputStream(destFile); + destination.getChannel().transferFrom(source.getChannel(), 0, source.getChannel().size()); + } catch (Exception e) { + FileLog.e("tmessages", e); + return false; + } finally { + if (source != null) { + source.close(); + } + if (destination != null) { + destination.close(); + } + } + return true; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/Animator10.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/Animator10.java similarity index 99% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/Animator10.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/Animator10.java index 31a6d756b..a4661919f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/Animator10.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/Animator10.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; import android.view.animation.Interpolator; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/AnimatorListenerAdapter10.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/AnimatorListenerAdapter10.java similarity index 96% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/AnimatorListenerAdapter10.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/AnimatorListenerAdapter10.java index 42349cd13..70b3aec39 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/AnimatorListenerAdapter10.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/AnimatorListenerAdapter10.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; public abstract class AnimatorListenerAdapter10 implements Animator10.AnimatorListener, Animator10.AnimatorPauseListener { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/AnimatorSet10.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/AnimatorSet10.java similarity index 99% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/AnimatorSet10.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/AnimatorSet10.java index 274dd6041..d9f63ef4b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/AnimatorSet10.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/AnimatorSet10.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; import android.view.animation.Interpolator; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/FloatEvaluator.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/FloatEvaluator.java similarity index 95% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/FloatEvaluator.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/FloatEvaluator.java index 2591ebf74..856f4acd4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/FloatEvaluator.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/FloatEvaluator.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; public class FloatEvaluator implements TypeEvaluator { public Float evaluate(float fraction, Number startValue, Number endValue) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/FloatKeyframeSet.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/FloatKeyframeSet.java similarity index 98% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/FloatKeyframeSet.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/FloatKeyframeSet.java index 52d0da5fe..691b44b31 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/FloatKeyframeSet.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/FloatKeyframeSet.java @@ -14,11 +14,11 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; import android.view.animation.Interpolator; -import org.telegram.ui.Animation.Keyframe.FloatKeyframe; +import org.telegram.android.Animation.Keyframe.FloatKeyframe; import java.util.ArrayList; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/FloatProperty10.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/FloatProperty10.java similarity index 95% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/FloatProperty10.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/FloatProperty10.java index 1a05b3bce..c02fb0f41 100755 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/FloatProperty10.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/FloatProperty10.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; public abstract class FloatProperty10 extends Property { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/IntEvaluator.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/IntEvaluator.java similarity index 95% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/IntEvaluator.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/IntEvaluator.java index cd3a19ebe..3f77f57f8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/IntEvaluator.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/IntEvaluator.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; public class IntEvaluator implements TypeEvaluator { public Integer evaluate(float fraction, Integer startValue, Integer endValue) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/IntKeyframeSet.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/IntKeyframeSet.java similarity index 98% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/IntKeyframeSet.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/IntKeyframeSet.java index 9d58863c8..3135614fc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/IntKeyframeSet.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/IntKeyframeSet.java @@ -14,11 +14,11 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; import android.view.animation.Interpolator; -import org.telegram.ui.Animation.Keyframe.IntKeyframe; +import org.telegram.android.Animation.Keyframe.IntKeyframe; import java.util.ArrayList; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/IntProperty.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/IntProperty.java similarity index 95% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/IntProperty.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/IntProperty.java index 07e72511a..701b88209 100755 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/IntProperty.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/IntProperty.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; public abstract class IntProperty extends Property { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/Keyframe.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/Keyframe.java similarity index 99% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/Keyframe.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/Keyframe.java index cb71460cd..2e4027392 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/Keyframe.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/Keyframe.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; import android.view.animation.Interpolator; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/KeyframeSet.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/KeyframeSet.java similarity index 97% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/KeyframeSet.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/KeyframeSet.java index 98a34ff67..38739e211 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/KeyframeSet.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/KeyframeSet.java @@ -14,16 +14,16 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.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; +import org.telegram.android.Animation.Keyframe.IntKeyframe; +import org.telegram.android.Animation.Keyframe.FloatKeyframe; +import org.telegram.android.Animation.Keyframe.ObjectKeyframe; class KeyframeSet { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/NoSuchPropertyException.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/NoSuchPropertyException.java similarity index 95% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/NoSuchPropertyException.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/NoSuchPropertyException.java index a3c59a4f4..bc00a4052 100755 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/NoSuchPropertyException.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/NoSuchPropertyException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; public class NoSuchPropertyException extends RuntimeException { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/ObjectAnimator10.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/ObjectAnimator10.java similarity index 99% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/ObjectAnimator10.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/ObjectAnimator10.java index 9f7ea7369..d29f201c2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/ObjectAnimator10.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/ObjectAnimator10.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; import android.view.View; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/Property.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/Property.java similarity index 97% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/Property.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/Property.java index 96beb2109..cd5248bb6 100755 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/Property.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/Property.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; public abstract class Property { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/PropertyValuesHolder.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/PropertyValuesHolder.java similarity index 99% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/PropertyValuesHolder.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/PropertyValuesHolder.java index 0420fb7b1..25dfa8385 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/PropertyValuesHolder.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/PropertyValuesHolder.java @@ -14,9 +14,8 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/ReflectiveProperty.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/ReflectiveProperty.java similarity index 99% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/ReflectiveProperty.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/ReflectiveProperty.java index ce487b4ff..7b4189822 100755 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/ReflectiveProperty.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/ReflectiveProperty.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/TypeEvaluator.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/TypeEvaluator.java similarity index 94% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/TypeEvaluator.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/TypeEvaluator.java index db5769e0c..d70e28400 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/TypeEvaluator.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/TypeEvaluator.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; public interface TypeEvaluator { T evaluate(float fraction, T startValue, T endValue); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/ValueAnimator.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/ValueAnimator.java similarity index 99% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/ValueAnimator.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/ValueAnimator.java index 168ff1dac..3b0c29130 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/ValueAnimator.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/ValueAnimator.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; import android.os.Looper; import android.util.AndroidRuntimeException; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Animation/View10.java b/TMessagesProj/src/main/java/org/telegram/android/Animation/View10.java similarity index 99% rename from TMessagesProj/src/main/java/org/telegram/ui/Animation/View10.java rename to TMessagesProj/src/main/java/org/telegram/android/Animation/View10.java index 4a89fe9c5..2cd59cb54 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Animation/View10.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Animation/View10.java @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package org.telegram.ui.Animation; +package org.telegram.android.Animation; import android.graphics.Camera; import android.graphics.Matrix; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/AnimationCompat/AnimatorListenerAdapterProxy.java b/TMessagesProj/src/main/java/org/telegram/android/AnimationCompat/AnimatorListenerAdapterProxy.java similarity index 94% rename from TMessagesProj/src/main/java/org/telegram/ui/AnimationCompat/AnimatorListenerAdapterProxy.java rename to TMessagesProj/src/main/java/org/telegram/android/AnimationCompat/AnimatorListenerAdapterProxy.java index 2c416a110..4825db9ea 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/AnimationCompat/AnimatorListenerAdapterProxy.java +++ b/TMessagesProj/src/main/java/org/telegram/android/AnimationCompat/AnimatorListenerAdapterProxy.java @@ -6,14 +6,14 @@ * Copyright Nikolai Kudashov, 2013-2014. */ -package org.telegram.ui.AnimationCompat; +package org.telegram.android.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; +import org.telegram.android.Animation.Animator10; +import org.telegram.android.Animation.AnimatorListenerAdapter10; +import org.telegram.android.Animation.View10; public class AnimatorListenerAdapterProxy { protected Object animatorListenerAdapter; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/AnimationCompat/AnimatorSetProxy.java b/TMessagesProj/src/main/java/org/telegram/android/AnimationCompat/AnimatorSetProxy.java similarity index 93% rename from TMessagesProj/src/main/java/org/telegram/ui/AnimationCompat/AnimatorSetProxy.java rename to TMessagesProj/src/main/java/org/telegram/android/AnimationCompat/AnimatorSetProxy.java index 50ac0c1d1..18c6b12da 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/AnimationCompat/AnimatorSetProxy.java +++ b/TMessagesProj/src/main/java/org/telegram/android/AnimationCompat/AnimatorSetProxy.java @@ -6,17 +6,17 @@ * Copyright Nikolai Kudashov, 2013-2014. */ -package org.telegram.ui.AnimationCompat; +package org.telegram.android.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 org.telegram.android.Animation.Animator10; +import org.telegram.android.Animation.AnimatorListenerAdapter10; +import org.telegram.android.Animation.AnimatorSet10; +import org.telegram.android.Animation.View10; import java.lang.reflect.Array; import java.util.ArrayList; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/AnimationCompat/ObjectAnimatorProxy.java b/TMessagesProj/src/main/java/org/telegram/android/AnimationCompat/ObjectAnimatorProxy.java similarity index 94% rename from TMessagesProj/src/main/java/org/telegram/ui/AnimationCompat/ObjectAnimatorProxy.java rename to TMessagesProj/src/main/java/org/telegram/android/AnimationCompat/ObjectAnimatorProxy.java index c9fd8cf18..3210bbff9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/AnimationCompat/ObjectAnimatorProxy.java +++ b/TMessagesProj/src/main/java/org/telegram/android/AnimationCompat/ObjectAnimatorProxy.java @@ -6,15 +6,15 @@ * Copyright Nikolai Kudashov, 2013-2014. */ -package org.telegram.ui.AnimationCompat; +package org.telegram.android.AnimationCompat; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.view.animation.Interpolator; -import org.telegram.ui.Animation.AnimatorListenerAdapter10; -import org.telegram.ui.Animation.ObjectAnimator10; -import org.telegram.ui.Animation.View10; +import org.telegram.android.Animation.AnimatorListenerAdapter10; +import org.telegram.android.Animation.ObjectAnimator10; +import org.telegram.android.Animation.View10; public class ObjectAnimatorProxy { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/AnimationCompat/ViewProxy.java b/TMessagesProj/src/main/java/org/telegram/android/AnimationCompat/ViewProxy.java similarity index 98% rename from TMessagesProj/src/main/java/org/telegram/ui/AnimationCompat/ViewProxy.java rename to TMessagesProj/src/main/java/org/telegram/android/AnimationCompat/ViewProxy.java index 478398bee..e8dcf6ce8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/AnimationCompat/ViewProxy.java +++ b/TMessagesProj/src/main/java/org/telegram/android/AnimationCompat/ViewProxy.java @@ -6,11 +6,11 @@ * Copyright Nikolai Kudashov, 2013-2014. */ -package org.telegram.ui.AnimationCompat; +package org.telegram.android.AnimationCompat; import android.view.View; -import org.telegram.ui.Animation.View10; +import org.telegram.android.Animation.View10; public class ViewProxy { diff --git a/TMessagesProj/src/main/java/org/telegram/android/AutoMessageHeardReceiver.java b/TMessagesProj/src/main/java/org/telegram/android/AutoMessageHeardReceiver.java new file mode 100644 index 000000000..825bcd565 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/AutoMessageHeardReceiver.java @@ -0,0 +1,26 @@ +/* + * This is the source code of Telegram for Android v. 2.x.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2015. + */ + +package org.telegram.android; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +public class AutoMessageHeardReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + long dialog_id = intent.getLongExtra("dialog_id", 0); + int max_id = intent.getIntExtra("max_id", 0); + if (dialog_id == 0 || max_id == 0) { + return; + } + MessagesController.getInstance().markDialogAsRead(dialog_id, max_id, max_id, 0, 0, true, false); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/AutoMessageReplyReceiver.java b/TMessagesProj/src/main/java/org/telegram/android/AutoMessageReplyReceiver.java new file mode 100644 index 000000000..c8411de73 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/AutoMessageReplyReceiver.java @@ -0,0 +1,37 @@ +/* + * This is the source code of Telegram for Android v. 2.x.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2014. + */ + +package org.telegram.android; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.RemoteInput; + +public class AutoMessageReplyReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + Bundle remoteInput = RemoteInput.getResultsFromIntent(intent); + if (remoteInput == null) { + return; + } + CharSequence text = remoteInput.getCharSequence(NotificationsController.EXTRA_VOICE_REPLY); + if (text == null || text.length() == 0) { + return; + } + long dialog_id = intent.getLongExtra("dialog_id", 0); + int max_id = intent.getIntExtra("max_id", 0); + if (dialog_id == 0 || max_id == 0) { + return; + } + SendMessagesHelper.getInstance().sendMessage(text.toString(), dialog_id, null, null, true); + MessagesController.getInstance().markDialogAsRead(dialog_id, max_id, max_id, 0, 0, true, false); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/ContactsController.java b/TMessagesProj/src/main/java/org/telegram/android/ContactsController.java index 84ac04327..da56c872d 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/ContactsController.java +++ b/TMessagesProj/src/main/java/org/telegram/android/ContactsController.java @@ -14,6 +14,7 @@ import android.app.Activity; import android.content.ContentProviderOperation; import android.content.ContentProviderResult; import android.content.ContentResolver; +import android.content.ContentValues; import android.content.SharedPreferences; import android.database.Cursor; import android.net.Uri; @@ -37,7 +38,6 @@ 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 { @@ -168,7 +168,7 @@ public class ContactsController { 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()); + req.lang_code = LocaleController.getLocaleString(LocaleController.getInstance().getSystemDefaultLocale()); if (req.lang_code == null || req.lang_code.length() == 0) { req.lang_code = "en"; } @@ -202,7 +202,19 @@ public class ContactsController { public void checkAppAccount() { AccountManager am = AccountManager.get(ApplicationLoader.applicationContext); - Account[] accounts = am.getAccountsByType("org.telegram.account"); + Account[] accounts; + try { + accounts = am.getAccountsByType("org.telegram.account"); + if (accounts != null && accounts.length > 0) { + for (Account c : accounts) { + am.removeAccount(c, null, null); + } + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + + accounts = am.getAccountsByType("org.telegram.messenger"); boolean recreateAccount = false; if (UserConfig.isClientActivated()) { if (accounts.length == 1) { @@ -227,7 +239,7 @@ public class ContactsController { } if (UserConfig.isClientActivated()) { try { - currentAccount = new Account(UserConfig.getCurrentUser().phone, "org.telegram.account"); + currentAccount = new Account(UserConfig.getCurrentUser().phone, "org.telegram.messenger"); am.addAccountExplicitly(currentAccount, "", null); } catch (Exception e) { FileLog.e("tmessages", e); @@ -239,7 +251,7 @@ public class ContactsController { public void deleteAllAppAccounts() { try { AccountManager am = AccountManager.get(ApplicationLoader.applicationContext); - Account[] accounts = am.getAccountsByType("org.telegram.account"); + Account[] accounts = am.getAccountsByType("org.telegram.messenger"); for (Account c : accounts) { am.removeAccount(c, null, null); } @@ -1246,7 +1258,7 @@ public class ContactsController { private void performWriteContactsToPhoneBook() { final ArrayList contactsArray = new ArrayList<>(); contactsArray.addAll(contacts); - Utilities.photoBookQueue.postRunnable(new Runnable() { + Utilities.phoneBookQueue.postRunnable(new Runnable() { @Override public void run() { performWriteContactsToPhoneBookInternal(contactsArray); @@ -1303,7 +1315,7 @@ public class ContactsController { } for (final Integer uid : contactsTD) { - Utilities.photoBookQueue.postRunnable(new Runnable() { + Utilities.phoneBookQueue.postRunnable(new Runnable() { @Override public void run() { deleteContactFromPhoneBook(uid); @@ -1463,7 +1475,7 @@ public class ContactsController { builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI); builder.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0); builder.withValue(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/vnd.org.telegram.messenger.android.profile"); - builder.withValue(ContactsContract.Data.DATA1, "+" + user.phone); + builder.withValue(ContactsContract.Data.DATA1, user.id); builder.withValue(ContactsContract.Data.DATA2, "Telegram Profile"); builder.withValue(ContactsContract.Data.DATA3, "+" + user.phone); builder.withValue(ContactsContract.Data.DATA4, user.id); @@ -1496,6 +1508,22 @@ public class ContactsController { } } + protected void markAsContacted(final String contactId) { + if (contactId == null) { + return; + } + Utilities.phoneBookQueue.postRunnable(new Runnable() { + @Override + public void run() { + Uri uri = Uri.parse(contactId); + ContentValues values = new ContentValues(); + values.put(ContactsContract.Contacts.LAST_TIME_CONTACTED, System.currentTimeMillis()); + ContentResolver cr = ApplicationLoader.applicationContext.getContentResolver(); + cr.update(uri, values, null, null); + } + }); + } + public void addContact(TLRPC.User user) { if (user == null || user.phone == null) { return; @@ -1533,7 +1561,7 @@ public class ContactsController { // } for (final TLRPC.User u : res.users) { - Utilities.photoBookQueue.postRunnable(new Runnable() { + Utilities.phoneBookQueue.postRunnable(new Runnable() { @Override public void run() { addContactToPhoneBook(u, true); @@ -1546,7 +1574,7 @@ public class ContactsController { MessagesStorage.getInstance().putContacts(arrayList, false); if (u.phone != null && u.phone.length() > 0) { - String name = formatName(u.first_name, u.last_name); + CharSequence name = formatName(u.first_name, u.last_name); MessagesStorage.getInstance().applyPhoneBookUpdates(u.phone, ""); Contact contact = contactsBookSPhones.get(u.phone); if (contact != null) { @@ -1599,7 +1627,7 @@ public class ContactsController { return; } MessagesStorage.getInstance().deleteContacts(uids); - Utilities.photoBookQueue.postRunnable(new Runnable() { + Utilities.phoneBookQueue.postRunnable(new Runnable() { @Override public void run() { for (TLRPC.User user : users) { @@ -1610,7 +1638,7 @@ public class ContactsController { for (TLRPC.User user : users) { if (user.phone != null && user.phone.length() > 0) { - String name = ContactsController.formatName(user.first_name, user.last_name); + CharSequence name = ContactsController.formatName(user.first_name, user.last_name); MessagesStorage.getInstance().applyPhoneBookUpdates(user.phone, ""); Contact contact = contactsBookSPhones.get(user.phone); if (contact != null) { @@ -1772,22 +1800,37 @@ public class ContactsController { } public static String formatName(String firstName, String lastName) { - String result = ""; + /*if ((firstName == null || firstName.length() == 0) && (lastName == null || lastName.length() == 0)) { + return LocaleController.getString("HiddenName", R.string.HiddenName); + }*/ + if (firstName != null) { + firstName = firstName.trim(); + } + if (lastName != null) { + lastName = lastName.trim(); + } + StringBuilder result = new StringBuilder((firstName != null ? firstName.length() : 0) + (lastName != null ? lastName.length() : 0) + 1); if (LocaleController.nameDisplayOrder == 1) { - result = firstName; - if (result == null || result.length() == 0) { - result = lastName; - } else if (result.length() != 0 && lastName != null && lastName.length() != 0) { - result += " " + lastName; + if (firstName != null && firstName.length() > 0) { + result.append(firstName); + if (lastName != null && lastName.length() > 0) { + result.append(" "); + result.append(lastName); + } + } else if (lastName != null && lastName.length() > 0) { + result.append(lastName); } } else { - result = lastName; - if (result == null || result.length() == 0) { - result = firstName; - } else if (result.length() != 0 && firstName != null && firstName.length() != 0) { - result += " " + firstName; + if (lastName != null && lastName.length() > 0) { + result.append(lastName); + if (firstName != null && firstName.length() > 0) { + result.append(" "); + result.append(firstName); + } + } else if (firstName != null && firstName.length() > 0) { + result.append(firstName); } } - return result.trim(); + return result.toString(); } } diff --git a/TMessagesProj/src/main/java/org/telegram/android/Emoji.java b/TMessagesProj/src/main/java/org/telegram/android/Emoji.java index 08eb51906..988a94b9e 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/Emoji.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Emoji.java @@ -33,7 +33,8 @@ import org.telegram.messenger.ApplicationLoader; public class Emoji { private static HashMap rects = new HashMap<>(); - private static int drawImgSize, bigImgSize; + private static int drawImgSize; + private static int bigImgSize; private static boolean inited = false; private static Paint placeholderPaint; private static Bitmap emojiBmp[] = new Bitmap[5]; @@ -193,19 +194,19 @@ public class Emoji { static { int emojiFullSize; if (AndroidUtilities.density <= 1.0f) { - emojiFullSize = 30; + emojiFullSize = 32; } else if (AndroidUtilities.density <= 1.5f) { - emojiFullSize = 45; + emojiFullSize = 48; } else if (AndroidUtilities.density <= 2.0f) { - emojiFullSize = 60; + emojiFullSize = 64; } else { - emojiFullSize = 90; + emojiFullSize = 96; } drawImgSize = AndroidUtilities.dp(20); if (AndroidUtilities.isTablet()) { bigImgSize = AndroidUtilities.dp(40); } else { - bigImgSize = AndroidUtilities.dp(30); + bigImgSize = AndroidUtilities.dp(32); } for (int j = 1; j < data.length; j++) { @@ -220,7 +221,7 @@ public class Emoji { private static void loadEmoji(final int page) { try { - float scale = 1.0f; + float scale; int imageResize = 1; if (AndroidUtilities.density <= 1.0f) { scale = 2.0f; @@ -234,11 +235,29 @@ public class Emoji { scale = 3.0f; } - String imageName = String.format(Locale.US, "emoji%.01fx_%d.jpg", scale, page); - File imageFile = ApplicationLoader.applicationContext.getFileStreamPath(imageName); + String imageName; + File imageFile; + + try { + imageName = String.format(Locale.US, "emoji%.01fx_%d.jpg", scale, page); + imageFile = ApplicationLoader.applicationContext.getFileStreamPath(imageName); + if (imageFile.exists()) { + imageFile.delete(); + } + imageName = String.format(Locale.US, "emoji%.01fx_a_%d.jpg", scale, page); + imageFile = ApplicationLoader.applicationContext.getFileStreamPath(imageName); + if (imageFile.exists()) { + imageFile.delete(); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + + imageName = String.format(Locale.US, "v4_emoji%.01fx_%d.jpg", scale, page); + imageFile = ApplicationLoader.applicationContext.getFileStreamPath(imageName); if (!imageFile.exists()) { InputStream is = ApplicationLoader.applicationContext.getAssets().open("emoji/" + imageName); - Utilities.copyFile(is, imageFile); + AndroidUtilities.copyFile(is, imageFile); is.close(); } @@ -253,11 +272,11 @@ public class Emoji { 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); + imageName = String.format(Locale.US, "v4_emoji%.01fx_a_%d.jpg", scale, page); imageFile = ApplicationLoader.applicationContext.getFileStreamPath(imageName); if (!imageFile.exists()) { InputStream is = ApplicationLoader.applicationContext.getAssets().open("emoji/" + imageName); - Utilities.copyFile(is, imageFile); + AndroidUtilities.copyFile(is, imageFile); is.close(); } diff --git a/TMessagesProj/src/main/java/org/telegram/android/ImageLoader.java b/TMessagesProj/src/main/java/org/telegram/android/ImageLoader.java index fb6363697..9bbefe8e4 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/ImageLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/android/ImageLoader.java @@ -67,9 +67,12 @@ public class ImageLoader { private DispatchQueue cacheThumbOutQueue = new DispatchQueue("cacheThumbOutQueue"); private DispatchQueue thumbGeneratingQueue = new DispatchQueue("thumbGeneratingQueue"); private DispatchQueue imageLoadQueue = new DispatchQueue("imageLoadQueue"); - private DispatchQueue recycleQueue = new DispatchQueue("recycleQueue"); private ConcurrentHashMap fileProgresses = new ConcurrentHashMap<>(); private HashMap thumbGenerateTasks = new HashMap<>(); + private static byte[] bytes; + private static byte[] bytesThumb; + private static byte[] header = new byte[12]; + private static byte[] headerThumb = new byte[12]; private int currentHttpTasksCount = 0; private LinkedList httpFileLoadTasks = new LinkedList<>(); @@ -114,8 +117,24 @@ public class ImageLoader { try { URL downloadUrl = new URL(url); httpConnection = downloadUrl.openConnection(); + httpConnection.addRequestProperty("User-Agent", "Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/_BuildID_) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36"); + httpConnection.addRequestProperty("Referer", "google.com"); httpConnection.setConnectTimeout(5000); httpConnection.setReadTimeout(5000); + if (httpConnection instanceof HttpURLConnection) { + HttpURLConnection httpURLConnection = (HttpURLConnection) httpConnection; + httpURLConnection.setInstanceFollowRedirects(true); + int status = httpURLConnection.getResponseCode(); + if (status == HttpURLConnection.HTTP_MOVED_TEMP || status == HttpURLConnection.HTTP_MOVED_PERM || status == HttpURLConnection.HTTP_SEE_OTHER) { + String newUrl = httpURLConnection.getHeaderField("Location"); + String cookies = httpURLConnection.getHeaderField("Set-Cookie"); + downloadUrl = new URL(newUrl); + httpConnection = downloadUrl.openConnection(); + httpConnection.setRequestProperty("Cookie", cookies); + httpConnection.addRequestProperty("User-Agent", "Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/_BuildID_) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36"); + httpConnection.addRequestProperty("Referer", "google.com"); + } + } httpConnection.connect(); httpConnectionStream = httpConnection.getInputStream(); @@ -135,29 +154,31 @@ public class ImageLoader { FileLog.e("tmessages", e); } - try { - byte[] data = new byte[1024 * 4]; - while (true) { - if (isCancelled()) { - break; - } - try { - int readed = httpConnectionStream.read(data); - if (readed > 0) { - fileOutputStream.write(data, 0, readed); - } else if (readed == -1) { - done = true; - break; - } else { + if (httpConnectionStream != null) { + try { + byte[] data = new byte[1024 * 4]; + while (true) { + if (isCancelled()) { + break; + } + try { + int read = httpConnectionStream.read(data); + if (read > 0) { + fileOutputStream.write(data, 0, read); + } else if (read == -1) { + done = true; + break; + } else { + break; + } + } catch (Exception e) { + FileLog.e("tmessages", e); break; } - } catch (Exception e) { - FileLog.e("tmessages", e); - break; } + } catch (Throwable e) { + FileLog.e("tmessages", e); } - } catch (Throwable e) { - FileLog.e("tmessages", e); } try { @@ -173,7 +194,6 @@ public class ImageLoader { if (httpConnectionStream != null) { httpConnectionStream.close(); } - httpConnectionStream = null; } catch (Throwable e) { FileLog.e("tmessages", e); } @@ -233,12 +253,16 @@ public class ImageLoader { try { URL downloadUrl = new URL(cacheImage.httpUrl); httpConnection = downloadUrl.openConnection(); + httpConnection.addRequestProperty("User-Agent", "Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/_BuildID_) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36"); + httpConnection.addRequestProperty("Referer", "google.com"); httpConnection.setConnectTimeout(5000); httpConnection.setReadTimeout(5000); + if (httpConnection instanceof HttpURLConnection) { + ((HttpURLConnection) httpConnection).setInstanceFollowRedirects(true); + } if (!isCancelled()) { httpConnection.connect(); httpConnectionStream = httpConnection.getInputStream(); - fileOutputStream = new RandomAccessFile(cacheImage.tempFilePath, "rws"); } } catch (Throwable e) { @@ -258,37 +282,39 @@ public class ImageLoader { FileLog.e("tmessages", e); } - try { - byte[] data = new byte[1024 * 2]; - int totalLoaded = 0; - while (true) { - if (isCancelled()) { - break; - } - try { - int readed = httpConnectionStream.read(data); - if (readed > 0) { - totalLoaded += readed; - fileOutputStream.write(data, 0, readed); - if (imageSize != 0) { - reportProgress(totalLoaded / (float) imageSize); - } - } else if (readed == -1) { - done = true; - if (imageSize != 0) { - reportProgress(1.0f); - } - break; - } else { + if (httpConnectionStream != null) { + try { + byte[] data = new byte[1024 * 2]; + int totalLoaded = 0; + while (true) { + if (isCancelled()) { + break; + } + try { + int read = httpConnectionStream.read(data); + if (read > 0) { + totalLoaded += read; + fileOutputStream.write(data, 0, read); + if (imageSize != 0) { + reportProgress(totalLoaded / (float) imageSize); + } + } else if (read == -1) { + done = true; + if (imageSize != 0) { + reportProgress(1.0f); + } + break; + } else { + break; + } + } catch (Exception e) { + FileLog.e("tmessages", e); break; } - } catch (Exception e) { - FileLog.e("tmessages", e); - break; } + } catch (Throwable e) { + FileLog.e("tmessages", e); } - } catch (Throwable e) { - FileLog.e("tmessages", e); } } @@ -305,7 +331,6 @@ public class ImageLoader { if (httpConnectionStream != null) { httpConnectionStream.close(); } - httpConnectionStream = null; } catch (Throwable e) { FileLog.e("tmessages", e); } @@ -444,7 +469,6 @@ public class ImageLoader { Bitmap scaledBitmap = Bitmap.createScaledBitmap(originalBitmap, (int) (w / scaleFactor), (int) (h / scaleFactor), true); if (scaledBitmap != originalBitmap) { originalBitmap.recycle(); - callGC(); } originalBitmap = scaledBitmap; FileOutputStream stream = new FileOutputStream(thumbFile); @@ -507,17 +531,45 @@ public class ImageLoader { } Long mediaId = null; + boolean mediaIsVideo = false; Bitmap image = null; File cacheFileFinal = cacheImage.finalFilePath; boolean canDeleteFile = true; - boolean isWebp = false; + boolean useNativeWebpLoaded = false; - if (cacheFileFinal.toString().endsWith("webp")) { - isWebp = true; + if (Build.VERSION.SDK_INT < 18) { + RandomAccessFile randomAccessFile = null; + try { + randomAccessFile = new RandomAccessFile(cacheFileFinal, "r"); + byte[] bytes; + if (cacheImage.thumb) { + bytes = headerThumb; + } else { + bytes = header; + } + randomAccessFile.readFully(bytes, 0, bytes.length); + String str = new String(bytes); + if (str != null) { + str = str.toLowerCase(); + if (str.startsWith("riff") && str.endsWith("webp")) { + useNativeWebpLoaded = true; + } + } + randomAccessFile.close(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } finally { + if (randomAccessFile != null) { + try { + randomAccessFile.close(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + } } if (cacheImage.thumb) { - int blurType = 0; if (cacheImage.filter != null) { if (cacheImage.filter.contains("b2")) { @@ -537,33 +589,51 @@ public class ImageLoader { } } - if (image == null) { - if (isWebp) { - RandomAccessFile file = new RandomAccessFile(cacheFileFinal, "r"); - ByteBuffer buffer = file.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, cacheFileFinal.length()); - image = Utilities.loadWebpImage(buffer, buffer.limit(), null); - file.close(); + BitmapFactory.Options opts = new BitmapFactory.Options(); + opts.inSampleSize = 1; + + if (!useNativeWebpLoaded && Build.VERSION.SDK_INT > 10 && Build.VERSION.SDK_INT < 21) { + opts.inPurgeable = true; + } + + if (useNativeWebpLoaded) { + RandomAccessFile file = new RandomAccessFile(cacheFileFinal, "r"); + ByteBuffer buffer = file.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, cacheFileFinal.length()); + image = Utilities.loadWebpImage(buffer, buffer.limit(), null); + file.close(); + } else { + if (opts.inPurgeable) { + RandomAccessFile f = new RandomAccessFile(cacheFileFinal, "r"); + int len = (int) f.length(); + byte[] data = bytesThumb != null && bytesThumb.length >= len ? bytesThumb : null; + if (data == null) { + bytesThumb = data = new byte[len]; + } + f.readFully(data, 0, len); + image = BitmapFactory.decodeByteArray(data, 0, len, opts); } else { FileInputStream is = new FileInputStream(cacheFileFinal); - image = BitmapFactory.decodeStream(is, null, null); + image = BitmapFactory.decodeStream(is, null, opts); is.close(); } } + if (image == null) { - if (canDeleteFile && (cacheFileFinal.length() == 0 || cacheImage.filter == null)) { + if (cacheFileFinal.length() == 0 || cacheImage.filter == null) { cacheFileFinal.delete(); } } else { - if (image != null) { - if (blurType == 1) { - Utilities.blurBitmap(image, 3); - } else if (blurType == 2) { - Utilities.blurBitmap(image, 1); - } else if (blurType == 3) { - Utilities.blurBitmap(image, 7); - Utilities.blurBitmap(image, 7); - Utilities.blurBitmap(image, 7); - } + if (blurType == 1) { + Utilities.blurBitmap(image, 3, opts.inPurgeable ? 0 : 1); + } else if (blurType == 2) { + Utilities.blurBitmap(image, 1, opts.inPurgeable ? 0 : 1); + } else if (blurType == 3) { + Utilities.blurBitmap(image, 7, opts.inPurgeable ? 0 : 1); + Utilities.blurBitmap(image, 7, opts.inPurgeable ? 0 : 1); + Utilities.blurBitmap(image, 7, opts.inPurgeable ? 0 : 1); + } + if (blurType == 0 && opts.inPurgeable) { + Utilities.pinBitmap(image); } if (runtimeHack != null) { runtimeHack.trackFree(image.getRowBytes() * image.getHeight()); @@ -579,6 +649,14 @@ public class ImageLoader { int idx = cacheImage.httpUrl.indexOf(":", 8); if (idx >= 0) { mediaId = Long.parseLong(cacheImage.httpUrl.substring(8, idx)); + mediaIsVideo = false; + } + canDeleteFile = false; + } else if (cacheImage.httpUrl.startsWith("vthumb://")) { + int idx = cacheImage.httpUrl.indexOf(":", 9); + if (idx >= 0) { + mediaId = Long.parseLong(cacheImage.httpUrl.substring(9, idx)); + mediaIsVideo = true; } canDeleteFile = false; } else if (!cacheImage.httpUrl.startsWith("http")) { @@ -604,35 +682,44 @@ public class ImageLoader { } BitmapFactory.Options opts = new BitmapFactory.Options(); + opts.inSampleSize = 1; float w_filter = 0; float h_filter = 0; boolean blur = false; if (cacheImage.filter != null) { String args[] = cacheImage.filter.split("_"); - w_filter = Float.parseFloat(args[0]) * AndroidUtilities.density; - h_filter = Float.parseFloat(args[1]) * AndroidUtilities.density; - if (args.length > 2) { + if (args.length >= 2) { + w_filter = Float.parseFloat(args[0]) * AndroidUtilities.density; + h_filter = Float.parseFloat(args[1]) * AndroidUtilities.density; + } + if (cacheImage.filter.contains("b")) { blur = true; } - opts.inJustDecodeBounds = true; + if (w_filter != 0 && h_filter != 0) { + opts.inJustDecodeBounds = true; - if (mediaId != null) { - MediaStore.Images.Thumbnails.getThumbnail(ApplicationLoader.applicationContext.getContentResolver(), mediaId, MediaStore.Images.Thumbnails.MINI_KIND, opts); - } else { - FileInputStream is = new FileInputStream(cacheFileFinal); - image = BitmapFactory.decodeStream(is, null, opts); - is.close(); - } + if (mediaId != null) { + if (mediaIsVideo) { + MediaStore.Video.Thumbnails.getThumbnail(ApplicationLoader.applicationContext.getContentResolver(), mediaId, MediaStore.Video.Thumbnails.MINI_KIND, opts); + } else { + MediaStore.Images.Thumbnails.getThumbnail(ApplicationLoader.applicationContext.getContentResolver(), mediaId, MediaStore.Images.Thumbnails.MINI_KIND, opts); + } + } else { + FileInputStream is = new FileInputStream(cacheFileFinal); + image = BitmapFactory.decodeStream(is, null, opts); + is.close(); + } - float photoW = opts.outWidth; - float photoH = opts.outHeight; - float scaleFactor = Math.max(photoW / w_filter, photoH / h_filter); - if (scaleFactor < 1) { - scaleFactor = 1; + float photoW = opts.outWidth; + float photoH = opts.outHeight; + float scaleFactor = Math.max(photoW / w_filter, photoH / h_filter); + if (scaleFactor < 1) { + scaleFactor = 1; + } + opts.inJustDecodeBounds = false; + opts.inSampleSize = (int) scaleFactor; } - opts.inJustDecodeBounds = false; - opts.inSampleSize = (int)scaleFactor; } synchronized (sync) { if (isCancelled) { @@ -645,24 +732,39 @@ public class ImageLoader { } else { opts.inPreferredConfig = Bitmap.Config.RGB_565; } - //if (Build.VERSION.SDK_INT < 21) { - // opts.inPurgeable = true; - //} + if (!useNativeWebpLoaded && Build.VERSION.SDK_INT > 10 && Build.VERSION.SDK_INT < 21) { + opts.inPurgeable = true; + } opts.inDither = false; if (mediaId != null) { - image = MediaStore.Images.Thumbnails.getThumbnail(ApplicationLoader.applicationContext.getContentResolver(), mediaId, MediaStore.Images.Thumbnails.MINI_KIND, opts); + if (mediaIsVideo) { + image = MediaStore.Video.Thumbnails.getThumbnail(ApplicationLoader.applicationContext.getContentResolver(), mediaId, MediaStore.Video.Thumbnails.MINI_KIND, opts); + } else { + image = MediaStore.Images.Thumbnails.getThumbnail(ApplicationLoader.applicationContext.getContentResolver(), mediaId, MediaStore.Images.Thumbnails.MINI_KIND, opts); + } } if (image == null) { - if (isWebp) { + if (useNativeWebpLoaded) { RandomAccessFile file = new RandomAccessFile(cacheFileFinal, "r"); ByteBuffer buffer = file.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, cacheFileFinal.length()); image = Utilities.loadWebpImage(buffer, buffer.limit(), null); file.close(); } else { - FileInputStream is = new FileInputStream(cacheFileFinal); - image = BitmapFactory.decodeStream(is, null, opts); - is.close(); + if (opts.inPurgeable) { + RandomAccessFile f = new RandomAccessFile(cacheFileFinal, "r"); + int len = (int) f.length(); + byte[] data = bytes != null && bytes.length >= len ? bytes : null; + if (data == null) { + bytes = data = new byte[len]; + } + f.readFully(data, 0, len); + image = BitmapFactory.decodeByteArray(data, 0, len, opts); + } else { + FileInputStream is = new FileInputStream(cacheFileFinal); + image = BitmapFactory.decodeStream(is, null, opts); + is.close(); + } } } if (image == null) { @@ -670,23 +772,27 @@ public class ImageLoader { cacheFileFinal.delete(); } } else { + boolean blured = false; if (cacheImage.filter != null) { float bitmapW = image.getWidth(); float bitmapH = image.getHeight(); - if (bitmapW != w_filter && bitmapW > w_filter) { + if (!opts.inPurgeable && w_filter != 0 && bitmapW != w_filter && bitmapW > w_filter + 20) { float scaleFactor = bitmapW / w_filter; - Bitmap scaledBitmap = Bitmap.createScaledBitmap(image, (int)w_filter, (int)(bitmapH / scaleFactor), true); + Bitmap scaledBitmap = Bitmap.createScaledBitmap(image, (int) w_filter, (int) (bitmapH / scaleFactor), true); if (image != scaledBitmap) { image.recycle(); - callGC(); image = scaledBitmap; } } if (image != null && blur && bitmapH < 100 && bitmapW < 100) { - Utilities.blurBitmap(image, 3); + Utilities.blurBitmap(image, 3, opts.inPurgeable ? 0 : 1); + blured = true; } } - if (runtimeHack != null) { + if (!blured && opts.inPurgeable) { + Utilities.pinBitmap(image); + } + if (runtimeHack != null && image != null) { runtimeHack.trackFree(image.getRowBytes() * image.getHeight()); } } @@ -714,7 +820,6 @@ public class ImageLoader { runtimeHack.trackAlloc(image.getRowBytes() * image.getHeight()); } image.recycle(); - callGC(); } } final BitmapDrawable toSetFinal = toSet; @@ -753,7 +858,7 @@ public class ImageLoader { } try { Object res = trackAllocation.invoke(runtime, size); - return (res instanceof Boolean) ? (Boolean)res : true; + return (res instanceof Boolean) ? (Boolean) res : true; } catch (Exception e) { return false; } @@ -765,7 +870,7 @@ public class ImageLoader { } try { Object res = trackFree.invoke(runtime, size); - return (res instanceof Boolean) ? (Boolean)res : true; + return (res instanceof Boolean) ? (Boolean) res : true; } catch (Exception e) { return false; } @@ -778,8 +883,8 @@ public class ImageLoader { Method getRt = cl.getMethod("getRuntime", new Class[0]); Object[] objects = new Object[0]; runtime = getRt.invoke(null, objects); - trackAllocation = cl.getMethod("trackExternalAllocation", new Class[] {long.class}); - trackFree = cl.getMethod("trackExternalFree", new Class[] {long.class}); + trackAllocation = cl.getMethod("trackExternalAllocation", new Class[]{long.class}); + trackFree = cl.getMethod("trackExternalFree", new Class[]{long.class}); } catch (Exception e) { FileLog.e("tmessages", e); runtime = null; @@ -793,6 +898,7 @@ public class ImageLoader { protected String key; protected String url; protected String filter; + protected String ext; protected TLObject location; protected File finalFilePath; @@ -837,7 +943,7 @@ public class ImageLoader { imageReceiverArray.clear(); if (location != null) { if (location instanceof TLRPC.FileLocation) { - FileLoader.getInstance().cancelLoadFile((TLRPC.FileLocation) location); + FileLoader.getInstance().cancelLoadFile((TLRPC.FileLocation) location, ext); } else if (location instanceof TLRPC.Document) { FileLoader.getInstance().cancelLoadFile((TLRPC.Document) location); } @@ -872,7 +978,7 @@ public class ImageLoader { @Override public void run() { for (ImageReceiver imgView : finalImageReceiverArray) { - imgView.setImageBitmapByKey(image, key, thumb); + imgView.setImageBitmapByKey(image, key, thumb, false); } } }); @@ -891,6 +997,7 @@ public class ImageLoader { } private static volatile ImageLoader Instance = null; + public static ImageLoader getInstance() { ImageLoader localInstance = Instance; if (localInstance == null) { @@ -915,12 +1022,13 @@ public class ImageLoader { @Override protected int sizeOf(String key, BitmapDrawable bitmap) { Bitmap b = bitmap.getBitmap(); - if(Build.VERSION.SDK_INT < 12) { + if (Build.VERSION.SDK_INT < 12) { return b.getRowBytes() * b.getHeight(); } else { return b.getByteCount(); } } + @Override protected void entryRemoved(boolean evicted, String key, final BitmapDrawable oldBitmap, BitmapDrawable newBitmap) { if (ignoreRemoval != null && key != null && ignoreRemoval.equals(key)) { @@ -957,11 +1065,16 @@ public class ImageLoader { } @Override - public void fileDidUploaded(final String location, final TLRPC.InputFile inputFile, final TLRPC.InputEncryptedFile inputEncryptedFile) { + public void fileDidUploaded(final String location, final TLRPC.InputFile inputFile, final TLRPC.InputEncryptedFile inputEncryptedFile, final byte[] key, final byte[] iv) { Utilities.stageQueue.postRunnable(new Runnable() { @Override public void run() { - NotificationCenter.getInstance().postNotificationName(NotificationCenter.FileDidUpload, location, inputFile, inputEncryptedFile); + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + NotificationCenter.getInstance().postNotificationName(NotificationCenter.FileDidUpload, location, inputFile, inputEncryptedFile, key, iv); + } + }); fileProgresses.remove(location); } }); @@ -972,7 +1085,12 @@ public class ImageLoader { Utilities.stageQueue.postRunnable(new Runnable() { @Override public void run() { - NotificationCenter.getInstance().postNotificationName(NotificationCenter.FileDidFailUpload, location, isEncrypted); + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + NotificationCenter.getInstance().postNotificationName(NotificationCenter.FileDidFailUpload, location, isEncrypted); + } + }); fileProgresses.remove(location); } }); @@ -987,7 +1105,7 @@ public class ImageLoader { if (location != null) { if (MediaController.getInstance().canSaveToGallery() && telegramPath != null && finalFile != null && finalFile.exists() && (location.endsWith(".mp4") || location.endsWith(".jpg"))) { if (finalFile.toString().startsWith(telegramPath.toString())) { - Utilities.addMediaToGallery(finalFile.toString()); + AndroidUtilities.addMediaToGallery(finalFile.toString()); } } } @@ -1188,17 +1306,6 @@ public class ImageLoader { } } - public void callGC() { - if (Build.VERSION.SDK_INT > 13) { - recycleQueue.postRunnable(new Runnable() { - @Override - public void run() { - System.gc(); - } - }); - } - } - public boolean decrementUseCount(String key) { Integer count = bitmapUseCounts.get(key); if (count == null) { @@ -1296,17 +1403,21 @@ public class ImageLoader { return memCache.get(key); } - public void replaceImageInCache(final String oldKey, final String newKey) { + public void replaceImageInCache(final String oldKey, final String newKey, final TLRPC.FileLocation newLocation) { AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { ArrayList arr = memCache.getFilterKeys(oldKey); if (arr != null) { for (String filter : arr) { - performReplace(oldKey + "@" + filter, newKey + "@" + filter); + String oldK = oldKey + "@" + filter; + String newK = newKey + "@" + filter; + performReplace(oldK, newK); + NotificationCenter.getInstance().postNotificationName(NotificationCenter.didReplacedPhotoInMemCache, oldK, newK, newLocation); } } else { performReplace(oldKey, newKey); + NotificationCenter.getInstance().postNotificationName(NotificationCenter.didReplacedPhotoInMemCache, oldKey, newKey, newLocation); } } }); @@ -1328,7 +1439,7 @@ public class ImageLoader { } } - private void createLoadOperationForImageReceiver(final ImageReceiver imageReceiver, final String key, final String url, final TLObject imageLocation, final String httpLocation, final String filter, final int size, final boolean cacheOnly, final int thumb) { + private void createLoadOperationForImageReceiver(final ImageReceiver imageReceiver, final String key, final String url, final String ext, final TLObject imageLocation, final String httpLocation, final String filter, final int size, final boolean cacheOnly, final int thumb) { if (imageReceiver == null || url == null || key == null) { return; } @@ -1384,6 +1495,11 @@ public class ImageLoader { if (idx >= 0) { cacheFile = new File(httpLocation.substring(idx + 1)); } + } else if (httpLocation.startsWith("vthumb://")) { + int idx = httpLocation.indexOf(":", 9); + if (idx >= 0) { + cacheFile = new File(httpLocation.substring(idx + 1)); + } } else { cacheFile = new File(httpLocation); } @@ -1439,6 +1555,7 @@ public class ImageLoader { img.key = key; img.filter = filter; img.httpUrl = httpLocation; + img.ext = ext; img.addImageReceiver(imageReceiver); if (onlyCache || cacheFile.exists()) { img.finalFilePath = cacheFile; @@ -1456,7 +1573,7 @@ public class ImageLoader { if (httpLocation == null) { if (imageLocation instanceof TLRPC.FileLocation) { TLRPC.FileLocation location = (TLRPC.FileLocation) imageLocation; - FileLoader.getInstance().loadFile(location, size, size == 0 || location.key != null || cacheOnly); + FileLoader.getInstance().loadFile(location, ext, size, size == 0 || location.key != null || cacheOnly); } else if (imageLocation instanceof TLRPC.Document) { FileLoader.getInstance().loadFile((TLRPC.Document) imageLocation, true, true); } @@ -1487,7 +1604,7 @@ public class ImageLoader { if (bitmapDrawable != null) { cancelLoadingForImageReceiver(imageReceiver, 0); if (!imageReceiver.isForcePreview()) { - imageReceiver.setImageBitmapByKey(bitmapDrawable, key, false); + imageReceiver.setImageBitmapByKey(bitmapDrawable, key, false, true); return; } } @@ -1497,7 +1614,7 @@ public class ImageLoader { if (thumbKey != null) { BitmapDrawable bitmapDrawable = memCache.get(thumbKey); if (bitmapDrawable != null) { - imageReceiver.setImageBitmapByKey(bitmapDrawable, thumbKey, true); + imageReceiver.setImageBitmapByKey(bitmapDrawable, thumbKey, true, true); cancelLoadingForImageReceiver(imageReceiver, 1); thumbSet = true; } @@ -1513,7 +1630,10 @@ public class ImageLoader { String thumbUrl = null; key = null; thumbKey = null; - String ext = null; + String ext = imageReceiver.getExt(); + if (ext == null) { + ext = "jpg"; + } if (httpLocation != null) { key = Utilities.MD5(httpLocation); url = key + "." + getHttpUrlExtension(httpLocation); @@ -1521,9 +1641,8 @@ public class ImageLoader { if (imageLocation instanceof TLRPC.FileLocation) { TLRPC.FileLocation location = (TLRPC.FileLocation) imageLocation; key = location.volume_id + "_" + location.local_id; - ext = "." + (location.ext != null ? location.ext : "jpg"); - url = key + ext; - if (location.ext != null || location.key != null || location.volume_id == Integer.MIN_VALUE && location.local_id < 0) { + url = key + "." + ext; + if (imageReceiver.getExt() != null || location.key != null || location.volume_id == Integer.MIN_VALUE && location.local_id < 0) { saveImageToCache = true; } } else if (imageLocation instanceof TLRPC.Document) { @@ -1532,10 +1651,19 @@ public class ImageLoader { return; } key = location.dc_id + "_" + location.id; - ext = ".webp"; - url = key + ext; + String docExt = FileLoader.getDocumentFileName(location); + int idx; + if (docExt == null || (idx = docExt.lastIndexOf(".")) == -1) { + docExt = ""; + } else { + docExt = docExt.substring(idx); + if (docExt.length() <= 1) { + docExt = ""; + } + } + url = key + docExt; if (thumbKey != null) { - thumbUrl = thumbKey + ext; + thumbUrl = thumbKey + "." + ext; } saveImageToCache = true; } @@ -1548,11 +1676,7 @@ public class ImageLoader { if (thumbLocation != null) { thumbKey = thumbLocation.volume_id + "_" + thumbLocation.local_id; - if (ext != null) { - thumbUrl = thumbKey + ext; - } else { - thumbUrl = thumbKey + "." + (thumbLocation.ext != null ? thumbLocation.ext : "jpg"); - } + thumbUrl = thumbKey + "." + ext; } String filter = imageReceiver.getFilter(); @@ -1565,10 +1689,10 @@ public class ImageLoader { } if (httpLocation != null) { - createLoadOperationForImageReceiver(imageReceiver, key, url, null, httpLocation, filter, 0, true, 0); + createLoadOperationForImageReceiver(imageReceiver, key, url, ext, null, httpLocation, filter, 0, true, 0); } else { - createLoadOperationForImageReceiver(imageReceiver, thumbKey, thumbUrl, thumbLocation, null, thumbFilter, 0, true, thumbSet ? 2 : 1); - createLoadOperationForImageReceiver(imageReceiver, key, url, imageLocation, null, filter, imageReceiver.getSize(), saveImageToCache || imageReceiver.getCacheOnly(), 0); + createLoadOperationForImageReceiver(imageReceiver, thumbKey, thumbUrl, ext, thumbLocation, null, thumbFilter, 0, true, thumbSet ? 2 : 1); + createLoadOperationForImageReceiver(imageReceiver, key, url, ext, imageLocation, null, filter, imageReceiver.getSize(), saveImageToCache || imageReceiver.getCacheOnly(), 0); } } @@ -1611,6 +1735,7 @@ public class ImageLoader { cacheImage.key = img.key; cacheImage.httpUrl = img.httpUrl; cacheImage.thumb = img.thumb; + cacheImage.ext = img.ext; cacheImage.cacheTask = task = new CacheOutTask(cacheImage); cacheImage.filter = img.filter; imageLoadingByKeys.put(cacheImage.key, cacheImage); @@ -1750,7 +1875,7 @@ public class ImageLoader { path = uri.getPath(); } else { try { - path = Utilities.getPath(uri); + path = AndroidUtilities.getPath(uri); } catch (Throwable e) { FileLog.e("tmessages", e); } @@ -1784,13 +1909,13 @@ public class ImageLoader { scaleFactor = 1; } bmOptions.inJustDecodeBounds = false; - bmOptions.inSampleSize = (int)scaleFactor; + bmOptions.inSampleSize = (int) scaleFactor; String exifPath = null; if (path != null) { exifPath = path; } else if (uri != null) { - exifPath = Utilities.getPath(uri); + exifPath = AndroidUtilities.getPath(uri); } Matrix matrix = null; @@ -1848,9 +1973,7 @@ public class ImageLoader { FileLog.e("tmessages", e); } finally { try { - if (parcelFD != null) { - parcelFD.close(); - } + parcelFD.close(); } catch (Throwable e) { FileLog.e("tmessages", e); } @@ -1860,8 +1983,25 @@ public class ImageLoader { return b; } + public static void fillPhotoSizeWithBytes(TLRPC.PhotoSize photoSize) { + if (photoSize == null || photoSize.bytes != null) { + return; + } + File file = FileLoader.getPathToAttach(photoSize, true); + try { + RandomAccessFile f = new RandomAccessFile(file, "r"); + int len = (int) f.length(); + if (len < 20000) { + photoSize.bytes = new byte[(int) f.length()]; + f.readFully(photoSize.bytes, 0, photoSize.bytes.length); + } + } catch (Throwable e) { + FileLog.e("tmessages", e); + } + } + 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; + Bitmap scaledBitmap; if (scaleFactor > 1 || scaleAnyway) { scaledBitmap = Bitmap.createScaledBitmap(bitmap, w, h, true); } else { @@ -1900,7 +2040,7 @@ public class ImageLoader { size.size = size.bytes.length; stream2.close(); } else { - size.size = (int)stream.getChannel().size(); + size.size = (int) stream.getChannel().size(); } stream.close(); if (scaledBitmap != bitmap) { @@ -1926,11 +2066,17 @@ public class ImageLoader { boolean scaleAnyway = false; float scaleFactor = Math.max(photoW / maxWidth, photoH / maxHeight); if (minWidth != 0 && minHeight != 0 && (photoW < minWidth || photoH < minHeight)) { - scaleFactor = Math.max(photoW / minWidth, photoH / minHeight); + if (photoW < minWidth && photoH > minHeight) { + scaleFactor = photoW / minWidth; + } else if (photoW > minWidth && photoH < minHeight) { + scaleFactor = photoH / minHeight; + } else { + scaleFactor = Math.max(photoW / minWidth, photoH / minHeight); + } scaleAnyway = true; } - int w = (int)(photoW / scaleFactor); - int h = (int)(photoH / scaleFactor); + int w = (int) (photoW / scaleFactor); + int h = (int) (photoH / scaleFactor); if (h == 0 || w == 0) { return null; } @@ -1978,12 +2124,6 @@ public class ImageLoader { } else if (message.media instanceof TLRPC.TL_messageMediaDocument) { if (message.media.document.thumb instanceof TLRPC.TL_photoCachedSize) { photoSize = message.media.document.thumb; - for (TLRPC.DocumentAttribute attribute : message.media.document.attributes) { - if (attribute instanceof TLRPC.TL_documentAttributeSticker) { - photoSize.location.ext = "webp"; - break; - } - } } } else if (message.media instanceof TLRPC.TL_messageMediaWebPage) { if (message.media.webpage.photo != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/android/ImageReceiver.java b/TMessagesProj/src/main/java/org/telegram/android/ImageReceiver.java index 6f50bdc59..b73778a24 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/ImageReceiver.java +++ b/TMessagesProj/src/main/java/org/telegram/android/ImageReceiver.java @@ -11,6 +11,7 @@ package org.telegram.android; import android.graphics.Bitmap; import android.graphics.BitmapShader; import android.graphics.Canvas; +import android.graphics.ColorFilter; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PorterDuff; @@ -33,18 +34,33 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg void didSetImage(ImageReceiver imageReceiver, boolean set, boolean thumb); } + private class SetImageBackup { + public TLObject fileLocation; + public String httpUrl; + public String filter; + public Drawable thumb; + public TLRPC.FileLocation thumbLocation; + public String thumbFilter; + public int size; + public boolean cacheOnly; + public String ext; + } + private View parentView; private Integer tag; private Integer thumbTag; private MessageObject parentMessageObject; private boolean canceledLoading; + private SetImageBackup setImageBackup; + private TLObject currentImageLocation; private String currentKey; private String currentThumbKey; private String currentHttpUrl; private String currentFilter; private String currentThumbFilter; + private String currentExt; private TLRPC.FileLocation currentThumbLocation; private int currentSize; private boolean currentCacheOnly; @@ -54,6 +70,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg private boolean needsQualityThumb; private boolean shouldGenerateQualityThumb; + private boolean invalidateAll; private int imageX, imageY, imageW, imageH; private Rect drawRegion = new Rect(); @@ -66,11 +83,16 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg private RectF roundRect; private RectF bitmapRect; private Matrix shaderMatrix; - private int alpha = 255; + private float overrideAlpha = 1.0f; private boolean isPressed; private int orientation; private boolean centerRotation; private ImageReceiverDelegate delegate; + private float currentAlpha; + private long lastUpdateAlphaTime; + private byte crossfadeAlpha = 1; + private boolean crossfadeWithThumb; + private ColorFilter colorFilter; public ImageReceiver() { @@ -85,27 +107,34 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg canceledLoading = true; } - public void setImage(TLObject path, String filter, Drawable thumb, boolean cacheOnly) { - setImage(path, null, filter, thumb, null, null, 0, cacheOnly); + public void setImage(TLObject path, String filter, Drawable thumb, String ext, boolean cacheOnly) { + setImage(path, null, filter, thumb, null, null, 0, ext, cacheOnly); } - public void setImage(TLObject path, String filter, Drawable thumb, int size, boolean cacheOnly) { - setImage(path, null, filter, thumb, null, null, size, cacheOnly); + public void setImage(TLObject path, String filter, Drawable thumb, int size, String ext, boolean cacheOnly) { + setImage(path, null, filter, thumb, null, null, size, ext, cacheOnly); } - public void setImage(String httpUrl, String filter, Drawable thumb, int size) { - setImage(null, httpUrl, filter, thumb, null, null, size, true); + public void setImage(String httpUrl, String filter, Drawable thumb, String ext, int size) { + setImage(null, httpUrl, filter, thumb, null, null, size, ext, true); } - public void setImage(TLObject fileLocation, String filter, TLRPC.FileLocation thumbLocation, String thumbFilter, boolean cacheOnly) { - setImage(fileLocation, null, filter, null, thumbLocation, thumbFilter, 0, cacheOnly); + public void setImage(TLObject fileLocation, String filter, TLRPC.FileLocation thumbLocation, String thumbFilter, String ext, boolean cacheOnly) { + setImage(fileLocation, null, filter, null, thumbLocation, thumbFilter, 0, ext, cacheOnly); } - public void setImage(TLObject fileLocation, String filter, TLRPC.FileLocation thumbLocation, String thumbFilter, int size, boolean cacheOnly) { - setImage(fileLocation, null, filter, null, thumbLocation, thumbFilter, size, cacheOnly); + public void setImage(TLObject fileLocation, String filter, TLRPC.FileLocation thumbLocation, String thumbFilter, int size, String ext, boolean cacheOnly) { + setImage(fileLocation, null, filter, null, thumbLocation, thumbFilter, size, ext, cacheOnly); } - public void setImage(TLObject fileLocation, String httpUrl, String filter, Drawable thumb, TLRPC.FileLocation thumbLocation, String thumbFilter, int size, boolean cacheOnly) { + public void setImage(TLObject fileLocation, String httpUrl, String filter, Drawable thumb, TLRPC.FileLocation thumbLocation, String thumbFilter, int size, String ext, boolean cacheOnly) { + if (setImageBackup != null) { + setImageBackup.fileLocation = null; + setImageBackup.httpUrl = null; + setImageBackup.thumbLocation = null; + setImageBackup.thumb = null; + } + if ((fileLocation == null && httpUrl == null && thumbLocation == null) || (fileLocation != null && !(fileLocation instanceof TLRPC.TL_fileLocation) && !(fileLocation instanceof TLRPC.TL_fileEncryptedLocation) @@ -113,6 +142,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg recycleBitmap(null, false); recycleBitmap(null, true); currentKey = null; + currentExt = ext; currentThumbKey = null; currentThumbFilter = null; currentImageLocation = null; @@ -120,13 +150,18 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg currentFilter = null; currentCacheOnly = false; staticThumb = thumb; + currentAlpha = 1; currentThumbLocation = null; currentSize = 0; currentImage = null; bitmapShader = null; ImageLoader.getInstance().cancelLoadingForImageReceiver(this, 0); if (parentView != null) { - parentView.invalidate(); + if (invalidateAll) { + parentView.invalidate(); + } else { + parentView.invalidate(imageX, imageY, imageX + imageW, imageY + imageH); + } } if (delegate != null) { delegate.didSetImage(this, currentImage != null || currentThumb != null || staticThumb != null, currentImage == null); @@ -134,6 +169,8 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg return; } + + if (!(thumbLocation instanceof TLRPC.TL_fileLocation)) { thumbLocation = null; } @@ -178,6 +215,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg currentThumbKey = thumbKey; currentKey = key; + currentExt = ext; currentImageLocation = fileLocation; currentHttpUrl = httpUrl; currentFilter = filter; @@ -187,6 +225,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg currentThumbLocation = thumbLocation; staticThumb = thumb; bitmapShader = null; + currentAlpha = 1.0f; if (delegate != null) { delegate.didSetImage(this, currentImage != null || currentThumb != null || staticThumb != null, currentImage == null); @@ -194,10 +233,18 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg ImageLoader.getInstance().loadImageForImageReceiver(this); if (parentView != null) { - parentView.invalidate(); + if (invalidateAll) { + parentView.invalidate(); + } else { + parentView.invalidate(imageX, imageY, imageX + imageW, imageY + imageH); + } } } + public void setColorFilter(ColorFilter filter) { + colorFilter = filter; + } + public void setDelegate(ImageReceiverDelegate delegate) { this.delegate = delegate; } @@ -215,6 +262,10 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg centerRotation = center; } + public void setInvalidateAll(boolean value) { + invalidateAll = value; + } + public int getOrientation() { return orientation; } @@ -230,6 +281,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg staticThumb = bitmap; currentThumbLocation = null; currentKey = null; + currentExt = null; currentThumbKey = null; currentImage = null; currentThumbFilter = null; @@ -239,11 +291,22 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg currentSize = 0; currentCacheOnly = false; bitmapShader = null; + if (setImageBackup != null) { + setImageBackup.fileLocation = null; + setImageBackup.httpUrl = null; + setImageBackup.thumbLocation = null; + setImageBackup.thumb = null; + } + currentAlpha = 1; if (delegate != null) { delegate.didSetImage(this, currentImage != null || currentThumb != null || staticThumb != null, currentImage == null); } if (parentView != null) { - parentView.invalidate(); + if (invalidateAll) { + parentView.invalidate(); + } else { + parentView.invalidate(imageX, imageY, imageX + imageW, imageY + imageH); + } } } @@ -256,6 +319,211 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg } } + public void onDetachedFromWindow() { + if (currentImageLocation != null || currentHttpUrl != null || currentThumbLocation != null || staticThumb != null) { + if (setImageBackup == null) { + setImageBackup = new SetImageBackup(); + } + setImageBackup.fileLocation = currentImageLocation; + setImageBackup.httpUrl = currentHttpUrl; + setImageBackup.filter = currentFilter; + setImageBackup.thumb = staticThumb; + setImageBackup.thumbLocation = currentThumbLocation; + setImageBackup.thumbFilter = currentThumbFilter; + setImageBackup.size = currentSize; + setImageBackup.ext = currentExt; + setImageBackup.cacheOnly = currentCacheOnly; + } + NotificationCenter.getInstance().removeObserver(this, NotificationCenter.didReplacedPhotoInMemCache); + clearImage(); + } + + public boolean onAttachedToWindow() { + NotificationCenter.getInstance().addObserver(this, NotificationCenter.didReplacedPhotoInMemCache); + if (setImageBackup != null && (setImageBackup.fileLocation != null || setImageBackup.httpUrl != null || setImageBackup.thumbLocation != null || setImageBackup.thumb != null)) { + setImage(setImageBackup.fileLocation, setImageBackup.httpUrl, setImageBackup.filter, setImageBackup.thumb, setImageBackup.thumbLocation, setImageBackup.thumbFilter, setImageBackup.size, setImageBackup.ext, setImageBackup.cacheOnly); + return true; + } + return false; + } + + private void drawDrawable(Canvas canvas, Drawable drawable, int alpha) { + if (drawable instanceof BitmapDrawable) { + BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; + + Paint paint = bitmapDrawable.getPaint(); + boolean hasFilter = paint != null && paint.getColorFilter() != null; + if (hasFilter && !isPressed) { + bitmapDrawable.setColorFilter(null); + } else if (!hasFilter && isPressed) { + bitmapDrawable.setColorFilter(new PorterDuffColorFilter(0xffdddddd, PorterDuff.Mode.MULTIPLY)); + } + if (colorFilter != null) { + bitmapDrawable.setColorFilter(colorFilter); + } + if (bitmapShader != null) { + drawRegion.set(imageX, imageY, imageX + imageW, imageY + imageH); + if (isVisible) { + roundRect.set(drawRegion); + shaderMatrix.reset(); + shaderMatrix.setRectToRect(bitmapRect, roundRect, Matrix.ScaleToFit.FILL); + bitmapShader.setLocalMatrix(shaderMatrix); + roundPaint.setAlpha(alpha); + canvas.drawRoundRect(roundRect, roundRadius, roundRadius, roundPaint); + } + } else { + int bitmapW; + int bitmapH; + if (orientation == 90 || orientation == 270) { + bitmapW = bitmapDrawable.getIntrinsicHeight(); + bitmapH = bitmapDrawable.getIntrinsicWidth(); + } else { + bitmapW = bitmapDrawable.getIntrinsicWidth(); + bitmapH = bitmapDrawable.getIntrinsicHeight(); + } + float scaleW = bitmapW / (float) imageW; + float scaleH = bitmapH / (float) imageH; + + if (isAspectFit) { + float scale = Math.max(scaleW, scaleH); + canvas.save(); + bitmapW /= scale; + bitmapH /= scale; + drawRegion.set(imageX + (imageW - bitmapW) / 2, imageY + (imageH - bitmapH) / 2, imageX + (imageW + bitmapW) / 2, imageY + (imageH + bitmapH) / 2); + bitmapDrawable.setBounds(drawRegion); + try { + bitmapDrawable.setAlpha(alpha); + bitmapDrawable.draw(canvas); + } catch (Exception e) { + if (bitmapDrawable == currentImage && currentKey != null) { + ImageLoader.getInstance().removeImage(currentKey); + currentKey = null; + } else if (bitmapDrawable == currentThumb && currentThumbKey != null) { + ImageLoader.getInstance().removeImage(currentThumbKey); + currentThumbKey = null; + } + setImage(currentImageLocation, currentHttpUrl, currentFilter, currentThumb, currentThumbLocation, currentThumbFilter, currentSize, currentExt, currentCacheOnly); + FileLog.e("tmessages", e); + } + canvas.restore(); + } else { + if (Math.abs(scaleW - scaleH) > 0.00001f) { + canvas.save(); + canvas.clipRect(imageX, imageY, imageX + imageW, imageY + imageH); + + if (orientation != 0) { + if (centerRotation) { + canvas.rotate(orientation, imageW / 2, imageH / 2); + } else { + canvas.rotate(orientation, 0, 0); + } + } + + if (bitmapW / scaleH > imageW) { + bitmapW /= scaleH; + drawRegion.set(imageX - (bitmapW - imageW) / 2, imageY, imageX + (bitmapW + imageW) / 2, imageY + imageH); + } else { + bitmapH /= scaleW; + drawRegion.set(imageX, imageY - (bitmapH - imageH) / 2, imageX + imageW, imageY + (bitmapH + imageH) / 2); + } + if (orientation == 90 || orientation == 270) { + int width = (drawRegion.right - drawRegion.left) / 2; + int height = (drawRegion.bottom - drawRegion.top) / 2; + int centerX = (drawRegion.right + drawRegion.left) / 2; + int centerY = (drawRegion.top + drawRegion.bottom) / 2; + bitmapDrawable.setBounds(centerX - height, centerY - width, centerX + height, centerY + width); + } else { + bitmapDrawable.setBounds(drawRegion); + } + if (isVisible) { + try { + bitmapDrawable.setAlpha(alpha); + bitmapDrawable.draw(canvas); + } catch (Exception e) { + if (bitmapDrawable == currentImage && currentKey != null) { + ImageLoader.getInstance().removeImage(currentKey); + currentKey = null; + } else if (bitmapDrawable == currentThumb && currentThumbKey != null) { + ImageLoader.getInstance().removeImage(currentThumbKey); + currentThumbKey = null; + } + setImage(currentImageLocation, currentHttpUrl, currentFilter, currentThumb, currentThumbLocation, currentThumbFilter, currentSize, currentExt, currentCacheOnly); + FileLog.e("tmessages", e); + } + } + + canvas.restore(); + } else { + canvas.save(); + if (orientation != 0) { + if (centerRotation) { + canvas.rotate(orientation, imageW / 2, imageH / 2); + } else { + canvas.rotate(orientation, 0, 0); + } + } + drawRegion.set(imageX, imageY, imageX + imageW, imageY + imageH); + if (orientation == 90 || orientation == 270) { + int width = (drawRegion.right - drawRegion.left) / 2; + int height = (drawRegion.bottom - drawRegion.top) / 2; + int centerX = (drawRegion.right + drawRegion.left) / 2; + int centerY = (drawRegion.top + drawRegion.bottom) / 2; + bitmapDrawable.setBounds(centerX - height, centerY - width, centerX + height, centerY + width); + } else { + bitmapDrawable.setBounds(drawRegion); + } + if (isVisible) { + try { + bitmapDrawable.setAlpha(alpha); + bitmapDrawable.draw(canvas); + } catch (Exception e) { + if (bitmapDrawable == currentImage && currentKey != null) { + ImageLoader.getInstance().removeImage(currentKey); + currentKey = null; + } else if (bitmapDrawable == currentThumb && currentThumbKey != null) { + ImageLoader.getInstance().removeImage(currentThumbKey); + currentThumbKey = null; + } + setImage(currentImageLocation, currentHttpUrl, currentFilter, currentThumb, currentThumbLocation, currentThumbFilter, currentSize, currentExt, currentCacheOnly); + FileLog.e("tmessages", e); + } + } + canvas.restore(); + } + } + } + } else { + drawRegion.set(imageX, imageY, imageX + imageW, imageY + imageH); + drawable.setBounds(drawRegion); + if (isVisible) { + try { + drawable.setAlpha(alpha); + drawable.draw(canvas); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + } + } + + private void checkAlphaAnimation() { + if (currentAlpha != 1) { + long currentTime = System.currentTimeMillis(); + currentAlpha += (currentTime - lastUpdateAlphaTime) / 150.0f; + if (currentAlpha > 1) { + currentAlpha = 1; + } + lastUpdateAlphaTime = System.currentTimeMillis(); + if (parentView != null) { + if (invalidateAll) { + parentView.invalidate(); + } else { + parentView.invalidate(imageX, imageY, imageX + imageW, imageY + imageH); + } + } + } + } + public boolean draw(Canvas canvas) { try { BitmapDrawable bitmapDrawable = null; @@ -267,161 +535,34 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg bitmapDrawable = currentThumb; } if (bitmapDrawable != null) { - Paint paint = bitmapDrawable.getPaint(); - boolean hasFilter = paint != null && paint.getColorFilter() != null; - if (hasFilter && !isPressed) { - bitmapDrawable.setColorFilter(null); - hasFilter = false; - } else if (!hasFilter && isPressed) { - bitmapDrawable.setColorFilter(new PorterDuffColorFilter(0xffdddddd, PorterDuff.Mode.MULTIPLY)); - hasFilter = true; - } - if (bitmapShader != null) { - drawRegion.set(imageX, imageY, imageX + imageW, imageY + imageH); - if (isVisible) { - roundRect.set(drawRegion); - shaderMatrix.reset(); - shaderMatrix.setRectToRect(bitmapRect, roundRect, Matrix.ScaleToFit.FILL); - bitmapShader.setLocalMatrix(shaderMatrix); - canvas.drawRoundRect(roundRect, roundRadius, roundRadius, roundPaint); + if (crossfadeAlpha != 0) { + if (crossfadeWithThumb && currentAlpha != 1.0f) { + Drawable thumbDrawable = null; + if (bitmapDrawable == currentImage) { + if (staticThumb != null) { + thumbDrawable = staticThumb; + } else if (currentThumb != null) { + thumbDrawable = currentThumb; + } + } else if (bitmapDrawable == currentThumb) { + if (staticThumb != null) { + thumbDrawable = staticThumb; + } + } + if (thumbDrawable != null) { + drawDrawable(canvas, thumbDrawable, (int) (overrideAlpha * 255)); + } } + drawDrawable(canvas, bitmapDrawable, (int) (overrideAlpha * currentAlpha * 255)); } else { - int bitmapW; - int bitmapH; - int originalW = bitmapDrawable.getIntrinsicWidth(); - int originalH = bitmapDrawable.getIntrinsicHeight(); - if (orientation == 90 || orientation == 270) { - bitmapW = bitmapDrawable.getIntrinsicHeight(); - bitmapH = bitmapDrawable.getIntrinsicWidth(); - } else { - bitmapW = bitmapDrawable.getIntrinsicWidth(); - bitmapH = bitmapDrawable.getIntrinsicHeight(); - } - float scaleW = bitmapW / (float) imageW; - float scaleH = bitmapH / (float) imageH; - - if (isAspectFit) { - float scale = Math.max(scaleW, scaleH); - canvas.save(); - bitmapW /= scale; - bitmapH /= scale; - drawRegion.set(imageX + (imageW - bitmapW) / 2, imageY + (imageH - bitmapH) / 2, imageX + (imageW + bitmapW) / 2, imageY + (imageH + bitmapH) / 2); - bitmapDrawable.setBounds(drawRegion); - try { - bitmapDrawable.setAlpha(alpha); - bitmapDrawable.draw(canvas); - } catch (Exception e) { - if (bitmapDrawable == currentImage && currentKey != null) { - ImageLoader.getInstance().removeImage(currentKey); - currentKey = null; - } else if (bitmapDrawable == currentThumb && currentThumbKey != null) { - ImageLoader.getInstance().removeImage(currentThumbKey); - currentThumbKey = null; - } - setImage(currentImageLocation, currentHttpUrl, currentFilter, currentThumb, currentThumbLocation, currentThumbFilter, currentSize, currentCacheOnly); - FileLog.e("tmessages", e); - } - canvas.restore(); - } else { - if (Math.abs(scaleW - scaleH) > 0.00001f) { - canvas.save(); - canvas.clipRect(imageX, imageY, imageX + imageW, imageY + imageH); - - if (orientation != 0) { - if (centerRotation) { - canvas.rotate(orientation, imageW / 2, imageH / 2); - } else { - canvas.rotate(orientation, 0, 0); - } - } - - if (bitmapW / scaleH > imageW) { - bitmapW /= scaleH; - originalW /= scaleH; - drawRegion.set(imageX - (bitmapW - imageW) / 2, imageY, imageX + (bitmapW + imageW) / 2, imageY + imageH); - } else { - bitmapH /= scaleW; - originalH /= scaleW; - drawRegion.set(imageX, imageY - (bitmapH - imageH) / 2, imageX + imageW, imageY + (bitmapH + imageH) / 2); - } - if (orientation == 90 || orientation == 270) { - int width = (drawRegion.right - drawRegion.left) / 2; - int height = (drawRegion.bottom - drawRegion.top) / 2; - int centerX = (drawRegion.right + drawRegion.left) / 2; - int centerY = (drawRegion.top + drawRegion.bottom) / 2; - bitmapDrawable.setBounds(centerX - height, centerY - width, centerX + height, centerY + width); - } else { - bitmapDrawable.setBounds(drawRegion); - } - if (isVisible) { - try { - bitmapDrawable.setAlpha(alpha); - bitmapDrawable.draw(canvas); - } catch (Exception e) { - if (bitmapDrawable == currentImage && currentKey != null) { - ImageLoader.getInstance().removeImage(currentKey); - currentKey = null; - } else if (bitmapDrawable == currentThumb && currentThumbKey != null) { - ImageLoader.getInstance().removeImage(currentThumbKey); - currentThumbKey = null; - } - setImage(currentImageLocation, currentHttpUrl, currentFilter, currentThumb, currentThumbLocation, currentThumbFilter, currentSize, currentCacheOnly); - FileLog.e("tmessages", e); - } - } - - canvas.restore(); - } else { - canvas.save(); - if (orientation != 0) { - if (centerRotation) { - canvas.rotate(orientation, imageW / 2, imageH / 2); - } else { - canvas.rotate(orientation, 0, 0); - } - } - drawRegion.set(imageX, imageY, imageX + imageW, imageY + imageH); - if (orientation == 90 || orientation == 270) { - int width = (drawRegion.right - drawRegion.left) / 2; - int height = (drawRegion.bottom - drawRegion.top) / 2; - int centerX = (drawRegion.right + drawRegion.left) / 2; - int centerY = (drawRegion.top + drawRegion.bottom) / 2; - bitmapDrawable.setBounds(centerX - height, centerY - width, centerX + height, centerY + width); - } else { - bitmapDrawable.setBounds(drawRegion); - } - if (isVisible) { - try { - bitmapDrawable.setAlpha(alpha); - bitmapDrawable.draw(canvas); - } catch (Exception e) { - if (bitmapDrawable == currentImage && currentKey != null) { - ImageLoader.getInstance().removeImage(currentKey); - currentKey = null; - } else if (bitmapDrawable == currentThumb && currentThumbKey != null) { - ImageLoader.getInstance().removeImage(currentThumbKey); - currentThumbKey = null; - } - setImage(currentImageLocation, currentHttpUrl, currentFilter, currentThumb, currentThumbLocation, currentThumbFilter, currentSize, currentCacheOnly); - FileLog.e("tmessages", e); - } - } - canvas.restore(); - } - } + drawDrawable(canvas, bitmapDrawable, (int) (overrideAlpha * 255)); } + + checkAlphaAnimation(); return true; } else if (staticThumb != null) { - drawRegion.set(imageX, imageY, imageX + imageW, imageY + imageH); - staticThumb.setBounds(drawRegion); - if (isVisible) { - try { - staticThumb.setAlpha(alpha); - staticThumb.draw(canvas); - } catch (Exception e) { - FileLog.e("tmessages", e); - } - } + drawDrawable(canvas, staticThumb, 255); + checkAlphaAnimation(); return true; } } catch (Exception e) { @@ -457,7 +598,11 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg } isVisible = value; if (invalidate && parentView != null) { - parentView.invalidate(); + if (invalidateAll) { + parentView.invalidate(); + } else { + parentView.invalidate(imageX, imageY, imageX + imageW, imageY + imageH); + } } } @@ -466,7 +611,11 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg } public void setAlpha(float value) { - alpha = (int)(value * 255.0f); + overrideAlpha = value; + } + + public void setCrossfadeAlpha(byte value) { + crossfadeAlpha = value; } public boolean hasImage() { @@ -512,6 +661,10 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg return imageH; } + public String getExt() { + return currentExt; + } + public boolean isInsideImage(float x, float y) { return x >= imageX && x <= imageX + imageW && y >= imageY && y <= imageY + imageH; } @@ -630,7 +783,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg } } - protected void setImageBitmapByKey(BitmapDrawable bitmap, String key, boolean thumb) { + protected void setImageBitmapByKey(BitmapDrawable bitmap, String key, boolean thumb, boolean memCache) { if (bitmap == null || key == null) { return; } @@ -646,17 +799,46 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg roundPaint.setShader(bitmapShader); bitmapRect.set(0, 0, object.getWidth(), object.getHeight()); } + + if (!memCache && !forcePreview) { + if (currentThumb == null && staticThumb == null || currentAlpha == 1.0f) { + currentAlpha = 0.0f; + lastUpdateAlphaTime = System.currentTimeMillis(); + crossfadeWithThumb = currentThumb != null || staticThumb != null; + } + } else { + currentAlpha = 1.0f; + } + if (parentView != null) { - parentView.invalidate(); + if (invalidateAll) { + parentView.invalidate(); + } else { + parentView.invalidate(imageX, imageY, imageX + imageW, imageY + imageH); + } } } else if (currentThumb == null && (currentImage == null || forcePreview)) { if (currentThumbKey == null || !key.equals(currentThumbKey)) { return; } ImageLoader.getInstance().incrementUseCount(currentThumbKey); + currentThumb = bitmap; + + if (!memCache && crossfadeAlpha != 2) { + currentAlpha = 0.0f; + lastUpdateAlphaTime = System.currentTimeMillis(); + crossfadeWithThumb = staticThumb != null && currentKey == null; + } else { + currentAlpha = 1.0f; + } + if (!(staticThumb instanceof BitmapDrawable) && parentView != null) { - parentView.invalidate(); + if (invalidateAll) { + parentView.invalidate(); + } else { + parentView.invalidate(imageX, imageY, imageX + imageW, imageY + imageH); + } } } @@ -696,7 +878,6 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg } if (canDelete) { bitmap.recycle(); - ImageLoader.getInstance().callGC(); } } if (thumb) { @@ -721,7 +902,31 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg staticThumb = null; } if (parentView != null) { - parentView.invalidate(); + if (invalidateAll) { + parentView.invalidate(); + } else { + parentView.invalidate(imageX, imageY, imageX + imageW, imageY + imageH); + } + } + } + } else if (id == NotificationCenter.didReplacedPhotoInMemCache) { + String oldKey = (String) args[0]; + if (currentKey != null && currentKey.equals(oldKey)) { + currentKey = (String) args[1]; + currentImageLocation = (TLRPC.FileLocation) args[2]; + } + if (currentThumbKey != null && currentThumbKey.equals(oldKey)) { + currentThumbKey = (String) args[1]; + currentThumbLocation = (TLRPC.FileLocation) args[2]; + } + if (setImageBackup != null) { + if (currentKey != null && currentKey.equals(oldKey)) { + currentKey = (String) args[1]; + currentImageLocation = (TLRPC.FileLocation) args[2]; + } + if (currentThumbKey != null && currentThumbKey.equals(oldKey)) { + currentThumbKey = (String) args[1]; + currentThumbLocation = (TLRPC.FileLocation) args[2]; } } } diff --git a/TMessagesProj/src/main/java/org/telegram/android/LocaleController.java b/TMessagesProj/src/main/java/org/telegram/android/LocaleController.java index 1d2094bc8..cf89fe20f 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/LocaleController.java +++ b/TMessagesProj/src/main/java/org/telegram/android/LocaleController.java @@ -23,7 +23,6 @@ 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.messenger.ApplicationLoader; import org.xmlpull.v1.XmlPullParser; @@ -309,6 +308,10 @@ public class LocaleController { } } + public Locale getSystemDefaultLocale() { + return systemDefaultLocale; + } + public static String getLocaleString(Locale locale) { if (locale == null) { return "en"; @@ -355,7 +358,7 @@ public class LocaleController { } File finalFile = new File(ApplicationLoader.applicationContext.getFilesDir(), languageCode + ".xml"); - if (!Utilities.copyFile(file, finalFile)) { + if (!AndroidUtilities.copyFile(file, finalFile)) { return false; } @@ -490,7 +493,6 @@ public class LocaleController { try { if (stream != null) { stream.close(); - stream = null; } } catch (Exception e) { FileLog.e("tmessages", e); @@ -508,7 +510,7 @@ public class LocaleController { return; } try { - Locale newLocale = null; + Locale newLocale; if (localeInfo.shortName != null) { String[] args = localeInfo.shortName.split("_"); if (args.length == 1) { @@ -607,11 +609,12 @@ public class LocaleController { } public static String formatString(String key, int res, Object... args) { - String value = getInstance().localeValues.get(key); - if (value == null) { - value = ApplicationLoader.applicationContext.getString(res); - } try { + String value = getInstance().localeValues.get(key); + if (value == null) { + value = ApplicationLoader.applicationContext.getString(res); + } + if (getInstance().currentLocale != null) { return String.format(getInstance().currentLocale, value, args); } else { @@ -677,50 +680,60 @@ public class LocaleController { } public static String formatDate(long date) { - Calendar rightNow = Calendar.getInstance(); - int day = rightNow.get(Calendar.DAY_OF_YEAR); - int year = rightNow.get(Calendar.YEAR); - rightNow.setTimeInMillis(date * 1000); - int dateDay = rightNow.get(Calendar.DAY_OF_YEAR); - int dateYear = rightNow.get(Calendar.YEAR); + try { + Calendar rightNow = Calendar.getInstance(); + int day = rightNow.get(Calendar.DAY_OF_YEAR); + int year = rightNow.get(Calendar.YEAR); + rightNow.setTimeInMillis(date * 1000); + int dateDay = rightNow.get(Calendar.DAY_OF_YEAR); + int dateYear = rightNow.get(Calendar.YEAR); - if (dateDay == day && year == dateYear) { - return formatterDay.format(new Date(date * 1000)); - } else if (dateDay + 1 == day && year == dateYear) { - return getString("Yesterday", R.string.Yesterday); - } else if (year == dateYear) { - return formatterMonth.format(new Date(date * 1000)); - } else { - return formatterYear.format(new Date(date * 1000)); + if (dateDay == day && year == dateYear) { + return formatterDay.format(new Date(date * 1000)); + } else if (dateDay + 1 == day && year == dateYear) { + return getString("Yesterday", R.string.Yesterday); + } else if (year == dateYear) { + return formatterMonth.format(new Date(date * 1000)); + } else { + return formatterYear.format(new Date(date * 1000)); + } + } catch (Exception e) { + FileLog.e("tmessages", e); } + return "LOC_ERR"; } public static String formatDateOnline(long date) { - Calendar rightNow = Calendar.getInstance(); - int day = rightNow.get(Calendar.DAY_OF_YEAR); - int year = rightNow.get(Calendar.YEAR); - rightNow.setTimeInMillis(date * 1000); - int dateDay = rightNow.get(Calendar.DAY_OF_YEAR); - int dateYear = rightNow.get(Calendar.YEAR); + try { + Calendar rightNow = Calendar.getInstance(); + int day = rightNow.get(Calendar.DAY_OF_YEAR); + int year = rightNow.get(Calendar.YEAR); + rightNow.setTimeInMillis(date * 1000); + int dateDay = rightNow.get(Calendar.DAY_OF_YEAR); + int dateYear = rightNow.get(Calendar.YEAR); - if (dateDay == day && year == dateYear) { - return String.format("%s %s %s", LocaleController.getString("LastSeen", R.string.LastSeen), LocaleController.getString("TodayAt", R.string.TodayAt), formatterDay.format(new Date(date * 1000))); - } else if (dateDay + 1 == day && year == dateYear) { - return String.format("%s %s %s", LocaleController.getString("LastSeen", R.string.LastSeen), LocaleController.getString("YesterdayAt", R.string.YesterdayAt), formatterDay.format(new Date(date * 1000))); - } else if (year == dateYear) { - String format = LocaleController.formatString("formatDateAtTime", R.string.formatDateAtTime, formatterMonth.format(new Date(date * 1000)), formatterDay.format(new Date(date * 1000))); - return String.format("%s %s", LocaleController.getString("LastSeenDate", R.string.LastSeenDate), format); - } else { - String format = LocaleController.formatString("formatDateAtTime", R.string.formatDateAtTime, formatterYear.format(new Date(date * 1000)), formatterDay.format(new Date(date * 1000))); - return String.format("%s %s", LocaleController.getString("LastSeenDate", R.string.LastSeenDate), format); + if (dateDay == day && year == dateYear) { + return String.format("%s %s %s", LocaleController.getString("LastSeen", R.string.LastSeen), LocaleController.getString("TodayAt", R.string.TodayAt), formatterDay.format(new Date(date * 1000))); + } else if (dateDay + 1 == day && year == dateYear) { + return String.format("%s %s %s", LocaleController.getString("LastSeen", R.string.LastSeen), LocaleController.getString("YesterdayAt", R.string.YesterdayAt), formatterDay.format(new Date(date * 1000))); + } else if (year == dateYear) { + String format = LocaleController.formatString("formatDateAtTime", R.string.formatDateAtTime, formatterMonth.format(new Date(date * 1000)), formatterDay.format(new Date(date * 1000))); + return String.format("%s %s", LocaleController.getString("LastSeenDate", R.string.LastSeenDate), format); + } else { + String format = LocaleController.formatString("formatDateAtTime", R.string.formatDateAtTime, formatterYear.format(new Date(date * 1000)), formatterDay.format(new Date(date * 1000))); + return String.format("%s %s", LocaleController.getString("LastSeenDate", R.string.LastSeenDate), format); + } + } catch (Exception e) { + FileLog.e("tmessages", e); } + return "LOC_ERR"; } private FastDateFormat createFormatter(Locale locale, String format, String defaultFormat) { if (format == null || format.length() == 0) { format = defaultFormat; } - FastDateFormat formatter = null; + FastDateFormat formatter; try { formatter = FastDateFormat.getInstance(format, locale); } catch (Exception e) { @@ -753,25 +766,30 @@ public class LocaleController { } public static String stringForMessageListDate(long date) { - Calendar rightNow = Calendar.getInstance(); - int day = rightNow.get(Calendar.DAY_OF_YEAR); - int year = rightNow.get(Calendar.YEAR); - rightNow.setTimeInMillis(date * 1000); - int dateDay = rightNow.get(Calendar.DAY_OF_YEAR); - int dateYear = rightNow.get(Calendar.YEAR); + try { + Calendar rightNow = Calendar.getInstance(); + int day = rightNow.get(Calendar.DAY_OF_YEAR); + int year = rightNow.get(Calendar.YEAR); + rightNow.setTimeInMillis(date * 1000); + int dateDay = rightNow.get(Calendar.DAY_OF_YEAR); + int dateYear = rightNow.get(Calendar.YEAR); - if (year != dateYear) { - return formatterYear.format(new Date(date * 1000)); - } else { - int dayDiff = dateDay - day; - if(dayDiff == 0 || dayDiff == -1 && (int)(System.currentTimeMillis() / 1000) - date < 60 * 60 * 8) { - return formatterDay.format(new Date(date * 1000)); - } else if(dayDiff > -7 && dayDiff <= -1) { - return formatterWeek.format(new Date(date * 1000)); + if (year != dateYear) { + return formatterYear.format(new Date(date * 1000)); } else { - return formatterMonth.format(new Date(date * 1000)); + int dayDiff = dateDay - day; + if(dayDiff == 0 || dayDiff == -1 && (int)(System.currentTimeMillis() / 1000) - date < 60 * 60 * 8) { + return formatterDay.format(new Date(date * 1000)); + } else if(dayDiff > -7 && dayDiff <= -1) { + return formatterWeek.format(new Date(date * 1000)); + } else { + return formatterMonth.format(new Date(date * 1000)); + } } + } catch (Exception e) { + FileLog.e("tmessages", e); } + return "LOC_ERR"; } public static String formatUserStatus(TLRPC.User user) { diff --git a/TMessagesProj/src/main/java/org/telegram/android/LruCache.java b/TMessagesProj/src/main/java/org/telegram/android/LruCache.java index 3ed7d3c64..d4870cc96 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/LruCache.java +++ b/TMessagesProj/src/main/java/org/telegram/android/LruCache.java @@ -107,7 +107,6 @@ public class LruCache { if (previous != null) { entryRemoved(false, key, previous, value); - ImageLoader.getInstance().callGC(); } trimToSize(maxSize, key); @@ -148,7 +147,6 @@ public class LruCache { entryRemoved(true, key, value, null); } - ImageLoader.getInstance().callGC(); } } @@ -183,7 +181,6 @@ public class LruCache { } entryRemoved(false, key, previous, null); - ImageLoader.getInstance().callGC(); } return previous; diff --git a/TMessagesProj/src/main/java/org/telegram/android/MediaController.java b/TMessagesProj/src/main/java/org/telegram/android/MediaController.java index 629268341..a0ff51259 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/MediaController.java +++ b/TMessagesProj/src/main/java/org/telegram/android/MediaController.java @@ -8,6 +8,7 @@ package org.telegram.android; +import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; import android.app.ProgressDialog; @@ -117,17 +118,27 @@ public class MediaController implements NotificationCenter.NotificationCenterDel MediaStore.Images.Media.ORIENTATION }; + private static final String[] projectionVideo = { + MediaStore.Video.Media._ID, + MediaStore.Video.Media.BUCKET_ID, + MediaStore.Video.Media.BUCKET_DISPLAY_NAME, + MediaStore.Video.Media.DATA, + MediaStore.Video.Media.DATE_TAKEN + }; + public static class AlbumEntry { public int bucketId; public String bucketName; public PhotoEntry coverPhoto; public ArrayList photos = new ArrayList<>(); public HashMap photosByIds = new HashMap<>(); + public boolean isVideo; - public AlbumEntry(int bucketId, String bucketName, PhotoEntry coverPhoto) { + public AlbumEntry(int bucketId, String bucketName, PhotoEntry coverPhoto, boolean isVideo) { this.bucketId = bucketId; this.bucketName = bucketName; this.coverPhoto = coverPhoto; + this.isVideo = isVideo; } public void addPhoto(PhotoEntry photoEntry) { @@ -144,13 +155,16 @@ public class MediaController implements NotificationCenter.NotificationCenterDel public int orientation; public String thumbPath; public String imagePath; + public boolean isVideo; + public CharSequence caption; - public PhotoEntry(int bucketId, int imageId, long dateTaken, String path, int orientation) { + public PhotoEntry(int bucketId, int imageId, long dateTaken, String path, int orientation, boolean isVideo) { this.bucketId = bucketId; this.imageId = imageId; this.dateTaken = dateTaken; this.path = path; this.orientation = orientation; + this.isVideo = isVideo; } } @@ -167,6 +181,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel public int date; public String thumbPath; public String imagePath; + public CharSequence caption; } public final static String MIME_TYPE = "video/avc"; @@ -178,6 +193,8 @@ public class MediaController implements NotificationCenter.NotificationCenterDel private final static int PROCESSOR_TYPE_TI = 5; private final Object videoConvertSync = new Object(); + private HashMap typingTimes = new HashMap<>(); + private SensorManager sensorManager; private Sensor proximitySensor; private boolean ignoreProximity; @@ -227,6 +244,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel private Timer progressTimer = null; private final Object progressTimerSync = new Object(); private boolean useFrontSpeaker; + private int buffersWrited; private AudioRecord audioRecorder = null; private TLRPC.TL_audio recordingAudio = null; @@ -255,7 +273,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel @Override public void run() { if (audioRecorder != null) { - ByteBuffer buffer = null; + ByteBuffer buffer; if (!recordBuffers.isEmpty()) { buffer = recordBuffers.get(0); recordBuffers.remove(0); @@ -550,6 +568,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel videoDownloadQueue.clear(); downloadQueueKeys.clear(); videoConvertQueue.clear(); + typingTimes.clear(); cancelVideoConvert(null); } @@ -674,7 +693,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel if (downloadObject.object instanceof TLRPC.Audio) { FileLoader.getInstance().loadFile((TLRPC.Audio)downloadObject.object, false); } else if (downloadObject.object instanceof TLRPC.PhotoSize) { - FileLoader.getInstance().loadFile((TLRPC.PhotoSize)downloadObject.object, false); + FileLoader.getInstance().loadFile((TLRPC.PhotoSize)downloadObject.object, null, false); } else if (downloadObject.object instanceof TLRPC.Video) { FileLoader.getInstance().loadFile((TLRPC.Video)downloadObject.object, false); } else if (downloadObject.object instanceof TLRPC.Document) { @@ -710,7 +729,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel if (downloadObject != null) { downloadQueueKeys.remove(fileName); if (state == 0 || state == 2) { - MessagesStorage.getInstance().removeFromDownloadQueue(downloadObject.id, downloadObject.type, state != 0); + MessagesStorage.getInstance().removeFromDownloadQueue(downloadObject.id, downloadObject.type, false /*state != 0*/); } if (downloadObject.type == AUTODOWNLOAD_MASK_PHOTO) { photoDownloadQueue.remove(downloadObject); @@ -975,6 +994,29 @@ public class MediaController implements NotificationCenter.NotificationCenterDel } listenerInProgress = false; processLaterArrays(); + try { + ArrayList delayedMessages = SendMessagesHelper.getInstance().getDelayedMessages(fileName); + if (delayedMessages != null) { + for (SendMessagesHelper.DelayedMessage delayedMessage : delayedMessages) { + if (delayedMessage.encryptedChat == null) { + long dialog_id = delayedMessage.obj.getDialogId(); + Long lastTime = typingTimes.get(dialog_id); + if (lastTime == null || lastTime + 4000 < System.currentTimeMillis()) { + if (delayedMessage.videoLocation != null) { + MessagesController.getInstance().sendTyping(dialog_id, 5, 0); + } else if (delayedMessage.documentLocation != null) { + MessagesController.getInstance().sendTyping(dialog_id, 3, 0); + } else if (delayedMessage.location != null) { + MessagesController.getInstance().sendTyping(dialog_id, 4, 0); + } + typingTimes.put(dialog_id, System.currentTimeMillis()); + } + } + } + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } } else if (id == NotificationCenter.messagesDeleted) { if (playingMessageObject != null) { ArrayList markAsDeletedMessages = (ArrayList)args[0]; @@ -1066,10 +1108,12 @@ public class MediaController implements NotificationCenter.NotificationCenterDel } catch (Exception e) { FileLog.e("tmessages", e); } + buffersWrited++; if (count > 0) { final long pcm = buffer.pcmOffset; - final int marker = buffer.finished == 1 ? buffer.size : -1; + final int marker = buffer.finished == 1 ? count : -1; + final int finalBuffersWrited = buffersWrited; AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { @@ -1078,6 +1122,9 @@ public class MediaController implements NotificationCenter.NotificationCenterDel if (audioTrackPlayer != null) { audioTrackPlayer.setNotificationMarkerPosition(1); } + if (finalBuffersWrited == 1) { + clenupPlayer(true); + } } } }); @@ -1100,12 +1147,17 @@ public class MediaController implements NotificationCenter.NotificationCenterDel }); } + private boolean isNearToSensor(float value) { + return value < 5.0f && value != proximitySensor.getMaximumRange(); + } + @Override public void onSensorChanged(SensorEvent event) { - if (proximitySensor != null && audioTrackPlayer == null && audioPlayer == null || isPaused || (useFrontSpeaker == (event.values[0] < proximitySensor.getMaximumRange() / 10))) { + FileLog.e("tmessages", "proximity changed to " + event.values[0]); + if (proximitySensor != null && audioTrackPlayer == null && audioPlayer == null || isPaused || (useFrontSpeaker == isNearToSensor(event.values[0]))) { return; } - boolean newValue = event.values[0] < proximitySensor.getMaximumRange() / 10; + boolean newValue = isNearToSensor(event.values[0]); try { if (newValue && NotificationsController.getInstance().audioManager.isWiredHeadsetOn()) { return; @@ -1190,6 +1242,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel } stopProgressTimer(); lastProgress = 0; + buffersWrited = 0; isPaused = false; MessageObject lastFile = playingMessageObject; playingMessageObject.audioProgress = 0.0f; @@ -1424,6 +1477,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel if (audioTrackPlayer == null && audioPlayer == null || messageObject == null || playingMessageObject == null || playingMessageObject != null && playingMessageObject.getId() != messageObject.getId()) { return false; } + stopProgressTimer(); try { if (audioPlayer != null) { audioPlayer.pause(); @@ -1440,11 +1494,12 @@ public class MediaController implements NotificationCenter.NotificationCenterDel } public boolean resumeAudio(MessageObject messageObject) { - startProximitySensor(); if (audioTrackPlayer == null && audioPlayer == null || messageObject == null || playingMessageObject == null || playingMessageObject != null && playingMessageObject.getId() != messageObject.getId()) { return false; } + startProximitySensor(); try { + startProgressTimer(); if (audioPlayer != null) { audioPlayer.start(); } else if (audioTrackPlayer != null) { @@ -1666,9 +1721,9 @@ public class MediaController implements NotificationCenter.NotificationCenterDel try { File destFile = null; if (type == 0) { - destFile = Utilities.generatePicturePath(); + destFile = AndroidUtilities.generatePicturePath(); } else if (type == 1) { - destFile = Utilities.generateVideoPath(); + destFile = AndroidUtilities.generateVideoPath(); } else if (type == 2) { File f = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); destFile = new File(f, name); @@ -1685,8 +1740,8 @@ public class MediaController implements NotificationCenter.NotificationCenterDel source = new FileInputStream(sourceFile).getChannel(); destination = new FileOutputStream(destFile).getChannel(); long size = source.size(); - for (long a = 0; a < size; a += 1024) { - destination.transferFrom(source, a, Math.min(1024, size - a)); + for (long a = 0; a < size; a += 4096) { + destination.transferFrom(source, a, Math.min(4096, size - a)); if (finalProgress != null) { if (lastProgress <= System.currentTimeMillis() - 500) { lastProgress = System.currentTimeMillis(); @@ -1717,7 +1772,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel } if (result && (type == 0 || type == 1)) { - Utilities.addMediaToGallery(Uri.fromFile(destFile)); + AndroidUtilities.addMediaToGallery(Uri.fromFile(destFile)); } } catch (Exception e) { FileLog.e("tmessages", e); @@ -1970,10 +2025,12 @@ public class MediaController implements NotificationCenter.NotificationCenterDel @Override public void run() { final ArrayList albumsSorted = new ArrayList<>(); + final ArrayList videoAlbumsSorted = new ArrayList<>(); HashMap albums = new HashMap<>(); AlbumEntry allPhotosAlbum = null; String cameraFolder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath() + "/" + "Camera/"; Integer cameraAlbumId = null; + Integer cameraAlbumVideoId = null; Cursor cursor = null; try { @@ -1998,10 +2055,10 @@ public class MediaController implements NotificationCenter.NotificationCenterDel continue; } - PhotoEntry photoEntry = new PhotoEntry(bucketId, imageId, dateTaken, path, orientation); + PhotoEntry photoEntry = new PhotoEntry(bucketId, imageId, dateTaken, path, orientation, false); if (allPhotosAlbum == null) { - allPhotosAlbum = new AlbumEntry(0, LocaleController.getString("AllPhotos", R.string.AllPhotos), photoEntry); + allPhotosAlbum = new AlbumEntry(0, LocaleController.getString("AllPhotos", R.string.AllPhotos), photoEntry, false); albumsSorted.add(0, allPhotosAlbum); } if (allPhotosAlbum != null) { @@ -2010,7 +2067,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel AlbumEntry albumEntry = albums.get(bucketId); if (albumEntry == null) { - albumEntry = new AlbumEntry(bucketId, bucketName, photoEntry); + albumEntry = new AlbumEntry(bucketId, bucketName, photoEntry, false); albums.put(bucketId, albumEntry); if (cameraAlbumId == null && cameraFolder != null && path != null && path.startsWith(cameraFolder)) { albumsSorted.add(0, albumEntry); @@ -2034,11 +2091,72 @@ public class MediaController implements NotificationCenter.NotificationCenterDel } } } + + try { + albums.clear(); + allPhotosAlbum = null; + cursor = MediaStore.Images.Media.query(ApplicationLoader.applicationContext.getContentResolver(), MediaStore.Video.Media.EXTERNAL_CONTENT_URI, projectionVideo, "", null, MediaStore.Video.Media.DATE_TAKEN + " DESC"); + if (cursor != null) { + int imageIdColumn = cursor.getColumnIndex(MediaStore.Video.Media._ID); + int bucketIdColumn = cursor.getColumnIndex(MediaStore.Video.Media.BUCKET_ID); + int bucketNameColumn = cursor.getColumnIndex(MediaStore.Video.Media.BUCKET_DISPLAY_NAME); + int dataColumn = cursor.getColumnIndex(MediaStore.Video.Media.DATA); + int dateColumn = cursor.getColumnIndex(MediaStore.Video.Media.DATE_TAKEN); + + while (cursor.moveToNext()) { + int imageId = cursor.getInt(imageIdColumn); + int bucketId = cursor.getInt(bucketIdColumn); + String bucketName = cursor.getString(bucketNameColumn); + String path = cursor.getString(dataColumn); + long dateTaken = cursor.getLong(dateColumn); + + if (path == null || path.length() == 0) { + continue; + } + + PhotoEntry photoEntry = new PhotoEntry(bucketId, imageId, dateTaken, path, 0, true); + + if (allPhotosAlbum == null) { + allPhotosAlbum = new AlbumEntry(0, LocaleController.getString("AllVideo", R.string.AllVideo), photoEntry, true); + videoAlbumsSorted.add(0, allPhotosAlbum); + } + if (allPhotosAlbum != null) { + allPhotosAlbum.addPhoto(photoEntry); + } + + AlbumEntry albumEntry = albums.get(bucketId); + if (albumEntry == null) { + albumEntry = new AlbumEntry(bucketId, bucketName, photoEntry, true); + albums.put(bucketId, albumEntry); + if (cameraAlbumVideoId == null && cameraFolder != null && path != null && path.startsWith(cameraFolder)) { + videoAlbumsSorted.add(0, albumEntry); + cameraAlbumVideoId = bucketId; + } else { + videoAlbumsSorted.add(albumEntry); + } + } + + albumEntry.addPhoto(photoEntry); + } + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } finally { + if (cursor != null) { + try { + cursor.close(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + } + final Integer cameraAlbumIdFinal = cameraAlbumId; + final Integer cameraAlbumVideoIdFinal = cameraAlbumVideoId; AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { - NotificationCenter.getInstance().postNotificationName(NotificationCenter.albumsDidLoaded, guid, albumsSorted, cameraAlbumIdFinal); + NotificationCenter.getInstance().postNotificationName(NotificationCenter.albumsDidLoaded, guid, albumsSorted, cameraAlbumIdFinal, videoAlbumsSorted, cameraAlbumVideoIdFinal); } }); } @@ -2082,6 +2200,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel } } + @SuppressLint("NewApi") public static MediaCodecInfo selectCodec(String mimeType) { int numCodecs = MediaCodecList.getCodecCount(); MediaCodecInfo lastCodecInfo = null; @@ -2118,6 +2237,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel } } + @SuppressLint("NewApi") public static int selectColorFormat(MediaCodecInfo codecInfo, String mimeType) { MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mimeType); int lastColorFormat = 0; @@ -2270,7 +2390,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel } private void checkConversionCanceled() throws Exception { - boolean cancelConversion = false; + boolean cancelConversion; synchronized (videoConvertSync) { cancelConversion = cancelCurrentVideoConversion; } @@ -2281,15 +2401,15 @@ public class MediaController implements NotificationCenter.NotificationCenterDel @TargetApi(16) private boolean convertVideo(final MessageObject messageObject) { - String videoPath = messageObject.messageOwner.videoEditedInfo.originalPath; - long startTime = messageObject.messageOwner.videoEditedInfo.startTime; - long endTime = messageObject.messageOwner.videoEditedInfo.endTime; - int resultWidth = messageObject.messageOwner.videoEditedInfo.resultWidth; - int resultHeight = messageObject.messageOwner.videoEditedInfo.resultHeight; - int rotationValue = messageObject.messageOwner.videoEditedInfo.rotationValue; - int originalWidth = messageObject.messageOwner.videoEditedInfo.originalWidth; - int originalHeight = messageObject.messageOwner.videoEditedInfo.originalHeight; - int bitrate = messageObject.messageOwner.videoEditedInfo.bitrate; + String videoPath = messageObject.videoEditedInfo.originalPath; + long startTime = messageObject.videoEditedInfo.startTime; + long endTime = messageObject.videoEditedInfo.endTime; + int resultWidth = messageObject.videoEditedInfo.resultWidth; + int resultHeight = messageObject.videoEditedInfo.resultHeight; + int rotationValue = messageObject.videoEditedInfo.rotationValue; + int originalWidth = messageObject.videoEditedInfo.originalWidth; + int originalHeight = messageObject.videoEditedInfo.originalHeight; + int bitrate = messageObject.videoEditedInfo.bitrate; int rotateRender = 0; File cacheFile = new File(messageObject.messageOwner.attachPath); @@ -2352,7 +2472,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel checkConversionCanceled(); if (resultWidth != originalWidth || resultHeight != originalHeight) { - int videoIndex = -5; + int videoIndex; videoIndex = selectTrack(extractor, false); if (videoIndex >= 0) { MediaCodec decoder = null; @@ -2368,7 +2488,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel int swapUV = 0; int videoTrackIndex = -5; - int colorFormat = 0; + int colorFormat; int processorType = PROCESSOR_TYPE_OTHER; String manufacturer = Build.MANUFACTURER.toLowerCase(); if (Build.VERSION.SDK_INT < 18) { @@ -2487,7 +2607,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel if (index == videoIndex) { int inputBufIndex = decoder.dequeueInputBuffer(TIMEOUT_USEC); if (inputBufIndex >= 0) { - ByteBuffer inputBuf = null; + ByteBuffer inputBuf; if (Build.VERSION.SDK_INT < 21) { inputBuf = decoderInputBuffers[inputBufIndex]; } else { @@ -2533,7 +2653,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel } else if (encoderStatus < 0) { throw new RuntimeException("unexpected result from encoder.dequeueOutputBuffer: " + encoderStatus); } else { - ByteBuffer encodedData = null; + ByteBuffer encodedData; if (Build.VERSION.SDK_INT < 21) { encodedData = encoderOutputBuffers[encoderStatus]; } else { @@ -2595,7 +2715,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel } else if (decoderStatus < 0) { throw new RuntimeException("unexpected result from decoder.dequeueOutputBuffer: " + decoderStatus); } else { - boolean doRender = false; + boolean doRender; if (Build.VERSION.SDK_INT >= 18) { doRender = info.size != 0; } else { @@ -2672,21 +2792,17 @@ public class MediaController implements NotificationCenter.NotificationCenterDel if (outputSurface != null) { outputSurface.release(); - outputSurface = null; } if (inputSurface != null) { inputSurface.release(); - inputSurface = null; } if (decoder != null) { decoder.stop(); decoder.release(); - decoder = null; } if (encoder != null) { encoder.stop(); encoder.release(); - encoder = null; } checkConversionCanceled(); @@ -2706,7 +2822,6 @@ public class MediaController implements NotificationCenter.NotificationCenterDel } finally { if (extractor != null) { extractor.release(); - extractor = null; } if (mediaMuxer != null) { try { @@ -2714,7 +2829,6 @@ public class MediaController implements NotificationCenter.NotificationCenterDel } catch (Exception e) { FileLog.e("tmessages", e); } - mediaMuxer = null; } FileLog.e("tmessages", "time = " + (System.currentTimeMillis() - time)); } diff --git a/TMessagesProj/src/main/java/org/telegram/android/MessageObject.java b/TMessagesProj/src/main/java/org/telegram/android/MessageObject.java index 525c91eba..ed916003e 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/MessageObject.java +++ b/TMessagesProj/src/main/java/org/telegram/android/MessageObject.java @@ -25,6 +25,7 @@ import org.telegram.messenger.TLRPC; import org.telegram.messenger.R; import org.telegram.messenger.UserConfig; import org.telegram.ui.Components.URLSpanNoUnderline; +import org.telegram.ui.Components.URLSpanNoUnderlineBold; import java.util.AbstractMap; import java.util.ArrayList; @@ -42,17 +43,19 @@ public class MessageObject { public TLRPC.Message messageOwner; public CharSequence messageText; public CharSequence linkDescription; + public CharSequence caption; public MessageObject replyMessageObject; public int type; public int contentType; public String dateKey; public String monthKey; - public boolean deleted = false; + public boolean deleted; public float audioProgress; public int audioProgressSec; public ArrayList photoThumbs; + public VideoEditedInfo videoEditedInfo; - private static TextPaint textPaint; + public static TextPaint textPaint; public int lastLineWidth; public int textWidth; public int textHeight; @@ -144,17 +147,35 @@ public class MessageObject { whoUser = MessagesController.getInstance().getUser(message.action.user_id); } if (whoUser != null && fromUser != null) { - if (isOut()) { - messageText = replaceWithLink(LocaleController.getString("ActionYouAddUser", R.string.ActionYouAddUser), "un2", whoUser); - } else if (message.action.user_id == UserConfig.getClientUserId()) { - messageText = replaceWithLink(LocaleController.getString("ActionAddUserYou", R.string.ActionAddUserYou), "un1", fromUser); + if (whoUser.id == fromUser.id) { + if (isOut()) { + messageText = LocaleController.getString("ActionAddUserSelfYou", R.string.ActionAddUserSelfYou); + } else { + messageText = replaceWithLink(LocaleController.getString("ActionAddUserSelf", R.string.ActionAddUserSelf), "un1", fromUser); + } } else { - messageText = replaceWithLink(LocaleController.getString("ActionAddUser", R.string.ActionAddUser), "un2", whoUser); - messageText = replaceWithLink(messageText, "un1", fromUser); + if (isOut()) { + messageText = replaceWithLink(LocaleController.getString("ActionYouAddUser", R.string.ActionYouAddUser), "un2", whoUser); + } else if (message.action.user_id == UserConfig.getClientUserId()) { + messageText = replaceWithLink(LocaleController.getString("ActionAddUserYou", R.string.ActionAddUserYou), "un1", fromUser); + } else { + messageText = replaceWithLink(LocaleController.getString("ActionAddUser", R.string.ActionAddUser), "un2", whoUser); + messageText = replaceWithLink(messageText, "un1", fromUser); + } } } else { messageText = LocaleController.getString("ActionAddUser", R.string.ActionAddUser).replace("un2", "").replace("un1", ""); } + } else if (message.action instanceof TLRPC.TL_messageActionChatJoinedByLink) { + if (fromUser != null) { + if (isOut()) { + messageText = LocaleController.getString("ActionInviteYou", R.string.ActionInviteYou); + } else { + messageText = replaceWithLink(LocaleController.getString("ActionInviteUser", R.string.ActionInviteUser), "un1", fromUser); + } + } else { + messageText = LocaleController.getString("ActionInviteUser", R.string.ActionInviteUser).replace("un1", ""); + } } else if (message.action instanceof TLRPC.TL_messageActionChatEditPhoto) { if (isOut()) { messageText = LocaleController.getString("ActionYouChangedPhoto", R.string.ActionYouChangedPhoto); @@ -279,7 +300,7 @@ public class MessageObject { messageText = LocaleController.getString("AttachPhoto", R.string.AttachPhoto); } else if (message.media instanceof TLRPC.TL_messageMediaVideo) { messageText = LocaleController.getString("AttachVideo", R.string.AttachVideo); - } else if (message.media instanceof TLRPC.TL_messageMediaGeo) { + } else if (message.media instanceof TLRPC.TL_messageMediaGeo || message.media instanceof TLRPC.TL_messageMediaVenue) { messageText = LocaleController.getString("AttachLocation", R.string.AttachLocation); } else if (message.media instanceof TLRPC.TL_messageMediaContact) { messageText = LocaleController.getString("AttachContact", R.string.AttachContact); @@ -312,9 +333,12 @@ public class MessageObject { if (message instanceof TLRPC.TL_message || message instanceof TLRPC.TL_messageForwarded_old2) { if (isMediaEmpty()) { contentType = type = 0; + if (messageText.length() == 0) { + messageText = "Empty message"; + } } else if (message.media instanceof TLRPC.TL_messageMediaPhoto) { contentType = type = 1; - } else if (message.media instanceof TLRPC.TL_messageMediaGeo) { + } else if (message.media instanceof TLRPC.TL_messageMediaGeo || message.media instanceof TLRPC.TL_messageMediaVenue) { contentType = 1; type = 4; } else if (message.media instanceof TLRPC.TL_messageMediaVideo) { @@ -332,9 +356,6 @@ public class MessageObject { type = 8; } else if (message.media.document.mime_type.equals("image/webp") && isSticker()) { type = 13; - if (messageOwner.media.document.thumb != null && messageOwner.media.document.thumb.location != null) { - messageOwner.media.document.thumb.location.ext = "webp"; - } } else { type = 9; } @@ -374,6 +395,12 @@ public class MessageObject { monthKey = String.format("%d_%02d", dateYear, dateMonth); } + if (messageOwner.message != null && messageOwner.id < 0 && messageOwner.message.length() > 6 && messageOwner.media instanceof TLRPC.TL_messageMediaVideo) { + videoEditedInfo = new VideoEditedInfo(); + videoEditedInfo.parseString(messageOwner.message); + } + + generateCaption(); if (generateLayout) { generateLayout(); } @@ -438,7 +465,7 @@ public class MessageObject { if (messageOwner.media.webpage.photo != null) { if (!update || photoThumbs == null) { photoThumbs = new ArrayList<>(messageOwner.media.webpage.photo.sizes); - } else if (photoThumbs != null && !photoThumbs.isEmpty()) { + } else if (!photoThumbs.isEmpty()) { for (TLRPC.PhotoSize photoObject : photoThumbs) { for (TLRPC.PhotoSize size : messageOwner.media.webpage.photo.sizes) { if (size instanceof TLRPC.TL_photoSizeEmpty) { @@ -459,7 +486,7 @@ public class MessageObject { public CharSequence replaceWithLink(CharSequence source, String param, TLRPC.User user) { String name = ContactsController.formatName(user.first_name, user.last_name); int start = TextUtils.indexOf(source, param); - URLSpanNoUnderline span = new URLSpanNoUnderline("" + user.id); + URLSpanNoUnderlineBold span = new URLSpanNoUnderlineBold("" + user.id); SpannableStringBuilder builder = new SpannableStringBuilder(TextUtils.replace(source, new String[]{param}, new String[]{name})); builder.setSpan(span, start, start + name.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); return builder; @@ -569,6 +596,41 @@ public class MessageObject { } } + public void generateCaption() { + if (caption != null) { + return; + } + if (messageOwner.media != null && messageOwner.media.caption != null && messageOwner.media.caption.length() > 0) { + caption = Emoji.replaceEmoji(messageOwner.media.caption, textPaint.getFontMetricsInt(), AndroidUtilities.dp(20)); + if (containsUrls(caption)) { + try { + Linkify.addLinks((Spannable) caption, Linkify.WEB_URLS); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + addUsernamesAndHashtags(caption); + } + } + } + + private void addUsernamesAndHashtags(CharSequence charSequence) { + try { + Pattern pattern = Pattern.compile("(^|\\s)@[a-zA-Z\\d_]{5,32}|(^|\\s)#[\\w\\.]+"); + Matcher matcher = pattern.matcher(charSequence); + while (matcher.find()) { + int start = matcher.start(); + int end = matcher.end(); + if (charSequence.charAt(start) != '@' && charSequence.charAt(start) != '#') { + start++; + } + URLSpanNoUnderline url = new URLSpanNoUnderline(charSequence.subSequence(start, end).toString()); + ((Spannable) charSequence).setSpan(url, start, end, 0); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + private void generateLayout() { if (type != 0 || messageOwner.to_id == null || messageText == null || messageText.length() == 0) { return; @@ -579,26 +641,19 @@ public class MessageObject { if (messageText instanceof Spannable && containsUrls(messageText)) { if (messageText.length() < 100) { - Linkify.addLinks((Spannable) messageText, Linkify.WEB_URLS | Linkify.PHONE_NUMBERS); - } else { - Linkify.addLinks((Spannable) messageText, Linkify.WEB_URLS); - } - - try { - Pattern pattern = Pattern.compile("(^|\\s)@[a-zA-Z\\d_]{5,32}|(^|\\s)#[\\w\\.]+"); - Matcher matcher = pattern.matcher(messageText); - while (matcher.find()) { - int start = matcher.start(); - int end = matcher.end(); - if (messageText.charAt(start) != '@' && messageText.charAt(start) != '#') { - start++; - } - URLSpanNoUnderline url = new URLSpanNoUnderline(messageText.subSequence(start, end).toString()); - ((Spannable) messageText).setSpan(url, start, end, 0); + try { + Linkify.addLinks((Spannable) messageText, Linkify.WEB_URLS | Linkify.PHONE_NUMBERS); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } else { + try { + Linkify.addLinks((Spannable) messageText, Linkify.WEB_URLS); + } catch (Exception e) { + FileLog.e("tmessages", e); } - } catch (Exception e) { - FileLog.e("tmessages", e); } + addUsernamesAndHashtags(messageText); } int maxWidth; @@ -616,7 +671,7 @@ public class MessageObject { } } - StaticLayout textLayout = null; + StaticLayout textLayout; try { textLayout = new StaticLayout(messageText, textPaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); @@ -735,7 +790,6 @@ public class MessageObject { if (a == blocksCount - 1) { lastLineWidth = lastLineWidthWithLeft; } - linesMaxWidth = linesMaxWidthWithLeft; } else if (a == blocksCount - 1) { lastLineWidth = linesMaxWidth; } @@ -763,10 +817,33 @@ public class MessageObject { return (messageOwner.flags & TLRPC.MESSAGE_FLAG_UNREAD) != 0; } + public boolean isContentUnread() { + return (messageOwner.flags & TLRPC.MESSAGE_FLAG_CONTENT_UNREAD) != 0; + } + public void setIsRead() { messageOwner.flags &= ~TLRPC.MESSAGE_FLAG_UNREAD; } + public int getUnradFlags() { + return getUnreadFlags(messageOwner); + } + + public static int getUnreadFlags(TLRPC.Message message) { + int flags = 0; + if ((message.flags & TLRPC.MESSAGE_FLAG_UNREAD) == 0) { + flags |= 1; + } + if ((message.flags & TLRPC.MESSAGE_FLAG_CONTENT_UNREAD) == 0) { + flags |= 2; + } + return flags; + } + + public void setContentIsRead() { + messageOwner.flags &= ~TLRPC.MESSAGE_FLAG_CONTENT_UNREAD; + } + public int getId() { return messageOwner.id; } @@ -782,18 +859,27 @@ public class MessageObject { messageOwner.media instanceof TLRPC.TL_messageMediaVideo); } - public static void setIsUnread(TLRPC.Message message, boolean unread) { - if (unread) { + public static void setUnreadFlags(TLRPC.Message message, int flag) { + if ((flag & 1) == 0) { message.flags |= TLRPC.MESSAGE_FLAG_UNREAD; } else { message.flags &= ~TLRPC.MESSAGE_FLAG_UNREAD; } + if ((flag & 2) == 0) { + message.flags |= TLRPC.MESSAGE_FLAG_CONTENT_UNREAD; + } else { + message.flags &= ~TLRPC.MESSAGE_FLAG_CONTENT_UNREAD; + } } public static boolean isUnread(TLRPC.Message message) { return (message.flags & TLRPC.MESSAGE_FLAG_UNREAD) != 0; } + public static boolean isContentUnread(TLRPC.Message message) { + return (message.flags & TLRPC.MESSAGE_FLAG_CONTENT_UNREAD) != 0; + } + public static boolean isOut(TLRPC.Message message) { return (message.flags & TLRPC.MESSAGE_FLAG_OUT) != 0; } @@ -859,6 +945,20 @@ public class MessageObject { return false; } + public static TLRPC.InputStickerSet getInputStickerSet(TLRPC.Message message) { + if (message.media != null && message.media.document != null) { + for (TLRPC.DocumentAttribute attribute : message.media.document.attributes) { + if (attribute instanceof TLRPC.TL_documentAttributeSticker) { + if (attribute.stickerset instanceof TLRPC.TL_inputStickerSetEmpty) { + return null; + } + return attribute.stickerset; + } + } + } + return null; + } + public String getStrickerChar() { if (messageOwner.media != null && messageOwner.media.document != null) { for (TLRPC.DocumentAttribute attribute : messageOwner.media.document.attributes) { @@ -911,8 +1011,8 @@ public class MessageObject { } return photoHeight + AndroidUtilities.dp(14); } else { - int photoHeight = 0; - int photoWidth = 0; + int photoHeight; + int photoWidth; if (AndroidUtilities.isTablet()) { photoWidth = (int) (AndroidUtilities.getMinTabletSide() * 0.7f); @@ -930,35 +1030,22 @@ public class MessageObject { if (currentPhotoObject != null) { float scale = (float) currentPhotoObject.w / (float) photoWidth; - int w = (int) (currentPhotoObject.w / scale); int h = (int) (currentPhotoObject.h / scale); - if (w == 0) { - w = AndroidUtilities.dp(100); - } if (h == 0) { h = AndroidUtilities.dp(100); } if (h > photoHeight) { - float scale2 = h; h = photoHeight; - scale2 /= h; - w = (int) (w / scale2); } else if (h < AndroidUtilities.dp(120)) { h = AndroidUtilities.dp(120); - float hScale = (float) currentPhotoObject.h / h; - if (currentPhotoObject.w / hScale < photoWidth) { - w = (int) (currentPhotoObject.w / hScale); - } } if (isSecretPhoto()) { if (AndroidUtilities.isTablet()) { - w = h = (int) (AndroidUtilities.getMinTabletSide() * 0.5f); + h = (int) (AndroidUtilities.getMinTabletSide() * 0.5f); } else { - w = h = (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.5f); + h = (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.5f); } } - - photoWidth = w; photoHeight = h; } return photoHeight + AndroidUtilities.dp(14); @@ -969,6 +1056,10 @@ public class MessageObject { return isStickerMessage(messageOwner); } + public TLRPC.InputStickerSet getInputStickerSet() { + return getInputStickerSet(messageOwner); + } + public boolean isForwarded() { return (messageOwner.flags & TLRPC.MESSAGE_FLAG_FWD) != 0; } diff --git a/TMessagesProj/src/main/java/org/telegram/android/MessagesController.java b/TMessagesProj/src/main/java/org/telegram/android/MessagesController.java index 6d2b29972..01b18bb9a 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/MessagesController.java +++ b/TMessagesProj/src/main/java/org/telegram/android/MessagesController.java @@ -10,9 +10,12 @@ package org.telegram.android; import android.app.Activity; import android.app.AlertDialog; +import android.app.ProgressDialog; +import android.content.DialogInterface; import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.os.Build; +import android.os.Bundle; import android.util.Base64; import android.util.SparseArray; @@ -22,19 +25,19 @@ import org.telegram.messenger.FileLog; import org.telegram.messenger.R; import org.telegram.messenger.RPCRequest; import org.telegram.messenger.SerializedData; -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.messenger.ApplicationLoader; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ChatActivity; +import org.telegram.ui.ProfileActivity; 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; import java.util.concurrent.Semaphore; @@ -45,13 +48,16 @@ public class MessagesController implements NotificationCenter.NotificationCenter private ConcurrentHashMap users = new ConcurrentHashMap<>(100, 1.0f, 2); private ConcurrentHashMap usersByUsernames = new ConcurrentHashMap<>(100, 1.0f, 2); + private HashMap exportedChats = new HashMap<>(); + public ArrayList dialogs = new ArrayList<>(); public ArrayList dialogsServerOnly = new ArrayList<>(); public ConcurrentHashMap dialogs_dict = new ConcurrentHashMap<>(100, 1.0f, 2); public HashMap dialogMessage = new HashMap<>(); public ConcurrentHashMap> printingUsers = new ConcurrentHashMap<>(20, 1.0f, 2); public HashMap printingStrings = new HashMap<>(); - public HashMap sendingTypings = new HashMap<>(); + public HashMap printingStringsTypes = new HashMap<>(); + public HashMap> sendingTypings = new HashMap<>(); public ConcurrentHashMap onlinePrivacy = new ConcurrentHashMap<>(20, 1.0f, 2); private int lastPrintingStringCount = 0; @@ -124,9 +130,11 @@ public class MessagesController implements NotificationCenter.NotificationCenter public static class PrintingUser { public long lastTime; public int userId; + public TLRPC.SendMessageAction action; } private static volatile MessagesController Instance = null; + public static MessagesController getInstance() { MessagesController localInstance = Instance; if (localInstance == null) { @@ -163,9 +171,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter byte[] bytes = Base64.decode(disabledFeaturesString, Base64.DEFAULT); if (bytes != null) { SerializedData data = new SerializedData(bytes); - int count = data.readInt32(); + int count = data.readInt32(false); for (int a = 0; a < count; a++) { - TLRPC.TL_disabledFeature feature = (TLRPC.TL_disabledFeature) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.TL_disabledFeature feature = TLRPC.TL_disabledFeature.TLdeserialize(data, data.readInt32(false), false); if (feature != null && feature.feature != null && feature.description != null) { disabledFeatures.add(feature); } @@ -198,7 +206,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter disabledFeature.serializeToStream(data); } String string = Base64.encodeToString(data.toByteArray(), Base64.DEFAULT); - if (string != null && string.length() != 0) { + if (string.length() != 0) { editor.putString("disabledFeatures", string); } } catch (Exception e) { @@ -221,7 +229,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter builder.setTitle("Oops!"); builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null); builder.setMessage(disabledFeature.description); - fragment.showAlertDialog(builder); + fragment.showDialog(builder.create()); } return false; } @@ -253,7 +261,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter if (user == null) { return null; } - TLRPC.InputUser inputUser = null; + TLRPC.InputUser inputUser; if (user.id == UserConfig.getClientUserId()) { inputUser = new TLRPC.TL_inputUserSelf(); } else if (user instanceof TLRPC.TL_userForeign || user instanceof TLRPC.TL_userRequest) { @@ -270,9 +278,8 @@ public class MessagesController implements NotificationCenter.NotificationCenter @Override public void didReceivedNotification(int id, Object... args) { if (id == NotificationCenter.FileDidUpload) { - final String location = (String)args[0]; - final TLRPC.InputFile file = (TLRPC.InputFile)args[1]; - final TLRPC.InputEncryptedFile encryptedFile = (TLRPC.InputEncryptedFile)args[2]; + final String location = (String) args[0]; + final TLRPC.InputFile file = (TLRPC.InputFile) args[1]; if (uploadingAvatar != null && uploadingAvatar.equals(location)) { TLRPC.TL_photos_uploadProfilePhoto req = new TLRPC.TL_photos_uploadProfilePhoto(); @@ -325,16 +332,14 @@ public class MessagesController implements NotificationCenter.NotificationCenter } } else if (id == NotificationCenter.FileDidFailUpload) { final String location = (String) args[0]; - final boolean enc = (Boolean) args[1]; - if (uploadingAvatar != null && uploadingAvatar.equals(location)) { uploadingAvatar = null; } } else if (id == NotificationCenter.messageReceivedByServer) { - Integer msgId = (Integer)args[0]; + Integer msgId = (Integer) args[0]; MessageObject obj = dialogMessage.get(msgId); if (obj != null) { - Integer newMsgId = (Integer)args[1]; + Integer newMsgId = (Integer) args[1]; dialogMessage.remove(msgId); dialogMessage.put(newMsgId, obj); obj.messageOwner.id = newMsgId; @@ -358,9 +363,6 @@ public class MessagesController implements NotificationCenter.NotificationCenter } NotificationCenter.getInstance().postNotificationName(NotificationCenter.dialogsNeedReload); } - } else { - NotificationCenter.getInstance().addObserver(this, NotificationCenter.FileDidLoaded); - NotificationCenter.getInstance().addObserver(this, NotificationCenter.FileDidFailedLoad); } } @@ -372,6 +374,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter SecretChatHelper.getInstance().cleanUp(); dialogs_dict.clear(); + exportedChats.clear(); dialogs.clear(); dialogsServerOnly.clear(); users.clear(); @@ -380,6 +383,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter dialogMessage.clear(); printingUsers.clear(); printingStrings.clear(); + printingStringsTypes.clear(); onlinePrivacy.clear(); totalDialogsCount = 0; lastPrintingStringCount = 0; @@ -427,7 +431,10 @@ public class MessagesController implements NotificationCenter.NotificationCenter } public TLRPC.User getUser(String username) { - return usersByUsernames.get(username); + if (username == null || username.length() == 0) { + return null; + } + return usersByUsernames.get(username.toLowerCase()); } public ConcurrentHashMap getUsers() { @@ -454,8 +461,8 @@ public class MessagesController implements NotificationCenter.NotificationCenter FileLog.e("tmessages", e); } if (result.size() == 2) { - chat = (TLRPC.EncryptedChat)result.get(0); - TLRPC.User user = (TLRPC.User)result.get(1); + chat = (TLRPC.EncryptedChat) result.get(0); + TLRPC.User user = (TLRPC.User) result.get(1); putEncryptedChat(chat, false); putUser(user, true); } @@ -463,6 +470,10 @@ public class MessagesController implements NotificationCenter.NotificationCenter return chat; } + public TLRPC.ExportedChatInvite getExportedInvite(int chat_id) { + return exportedChats.get(chat_id); + } + public boolean putUser(TLRPC.User user, boolean fromCache) { if (user == null) { return false; @@ -473,7 +484,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter usersByUsernames.remove(oldUser.username); } if (user.username != null && user.username.length() > 0) { - usersByUsernames.put(user.username, user); + usersByUsernames.put(user.username.toLowerCase(), user); } if (!fromCache) { users.put(user.id, user); @@ -564,7 +575,11 @@ public class MessagesController implements NotificationCenter.NotificationCenter } public void loadFullChat(final int chat_id, final int classGuid) { - if (loadingFullChats.contains(chat_id) || loadedFullChats.contains(chat_id)) { + loadFullChat(chat_id, classGuid, false); + } + + public void loadFullChat(final int chat_id, final int classGuid, boolean force) { + if (loadingFullChats.contains(chat_id) || !force && loadedFullChats.contains(chat_id)) { return; } loadingFullChats.add(chat_id); @@ -580,12 +595,13 @@ public class MessagesController implements NotificationCenter.NotificationCenter AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { - loadingFullChats.remove((Integer)chat_id); + exportedChats.put(chat_id, res.full_chat.exported_invite); + loadingFullChats.remove((Integer) chat_id); loadedFullChats.add(chat_id); putUsers(res.users, false); putChats(res.chats, false); - NotificationCenter.getInstance().postNotificationName(NotificationCenter.chatInfoDidLoaded, chat_id, res.full_chat.participants); + NotificationCenter.getInstance().postNotificationName(NotificationCenter.chatInfoDidLoaded, chat_id, res.full_chat.participants, classGuid); } }); } else { @@ -617,10 +633,10 @@ public class MessagesController implements NotificationCenter.NotificationCenter AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { - loadingFullUsers.remove((Integer)user.id); + loadingFullUsers.remove((Integer) user.id); loadedFullUsers.add(user.id); String names = user.first_name + user.last_name + user.username; - TLRPC.TL_userFull userFull = (TLRPC.TL_userFull)response; + TLRPC.TL_userFull userFull = (TLRPC.TL_userFull) response; ArrayList users = new ArrayList<>(); users.add(userFull.user); putUsers(users, false); @@ -634,7 +650,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { - loadingFullUsers.remove((Integer)user.id); + loadingFullUsers.remove((Integer) user.id); } }); } @@ -693,7 +709,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter MessagesStorage.lastPtsValue = pts; MessagesStorage.getInstance().saveDiffParams(MessagesStorage.lastSeqValue, MessagesStorage.lastPtsValue, MessagesStorage.lastDateValue, MessagesStorage.lastQtsValue); } else if (MessagesStorage.lastPtsValue != pts) { - if (gettingDifference || updatesStartWaitTimePts == 0 || updatesStartWaitTimePts != 0 && updatesStartWaitTimePts + 1500 > System.currentTimeMillis()) { + if (gettingDifference || updatesStartWaitTimePts == 0 || updatesStartWaitTimePts + 1500 > System.currentTimeMillis()) { FileLog.e("tmessages", "ADD UPDATE TO QUEUE pts = " + pts + " pts_count = " + pts_count); if (updatesStartWaitTimePts == 0) { updatesStartWaitTimePts = System.currentTimeMillis(); @@ -716,7 +732,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter } MessagesStorage.getInstance().saveDiffParams(MessagesStorage.lastSeqValue, MessagesStorage.lastPtsValue, MessagesStorage.lastDateValue, MessagesStorage.lastQtsValue); } else if (MessagesStorage.lastSeqValue != seq) { - if (gettingDifference || updatesStartWaitTimeSeq == 0 || updatesStartWaitTimeSeq != 0 && updatesStartWaitTimeSeq + 1500 > System.currentTimeMillis()) { + if (gettingDifference || updatesStartWaitTimeSeq == 0 || updatesStartWaitTimeSeq + 1500 > System.currentTimeMillis()) { FileLog.e("tmessages", "ADD UPDATE TO QUEUE seq = " + seq); if (updatesStartWaitTimeSeq == 0) { updatesStartWaitTimeSeq = System.currentTimeMillis(); @@ -809,7 +825,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter } }; int currentServerTime = ConnectionsManager.getInstance().getCurrentTime(); - Utilities.stageQueue.postRunnable(currentDeleteTaskRunnable, (long)Math.abs(currentServerTime - currentDeletingTaskTime) * 1000); + Utilities.stageQueue.postRunnable(currentDeleteTaskRunnable, (long) Math.abs(currentServerTime - currentDeletingTaskTime) * 1000); } } else { currentDeletingTaskTime = 0; @@ -830,14 +846,14 @@ public class MessagesController implements NotificationCenter.NotificationCenter TLRPC.TL_photos_getUserPhotos req = new TLRPC.TL_photos_getUserPhotos(); req.limit = count; req.offset = offset; - req.max_id = (int)max_id; + req.max_id = (int) max_id; req.user_id = getInputUser(user); long reqId = ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() { @Override public void run(TLObject response, TLRPC.TL_error error) { if (error == null) { TLRPC.photos_Photos res = (TLRPC.photos_Photos) response; - processLoadedUserPhotos(res, uid, offset, count, max_id, fromCache, classGuid); + processLoadedUserPhotos(res, uid, offset, count, max_id, false, classGuid); } } }); @@ -900,7 +916,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter ArrayList blocked = new ArrayList<>(); ArrayList users = null; if (error == null) { - final TLRPC.contacts_Blocked res = (TLRPC.contacts_Blocked)response; + final TLRPC.contacts_Blocked res = (TLRPC.contacts_Blocked) response; for (TLRPC.TL_contactBlocked contactBlocked : res.blocked) { blocked.add(contactBlocked.user_id); } @@ -948,9 +964,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter if (user == null) { return; } - if (user != null) { - user.photo = UserConfig.getCurrentUser().photo; - } + user.photo = UserConfig.getCurrentUser().photo; NotificationCenter.getInstance().postNotificationName(NotificationCenter.mainUserInfoChanged); NotificationCenter.getInstance().postNotificationName(NotificationCenter.updateInterfaces, MessagesController.UPDATE_MASK_ALL); ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() { @@ -1060,8 +1074,8 @@ public class MessagesController implements NotificationCenter.NotificationCenter } public void deleteDialog(final long did, int offset, final boolean onlyHistory) { - int lower_part = (int)did; - int high_id = (int)(did >> 32); + int lower_part = (int) did; + int high_id = (int) (did >> 32); if (offset == 0) { TLRPC.TL_dialog dialog = dialogs_dict.get(did); @@ -1275,15 +1289,72 @@ public class MessagesController implements NotificationCenter.NotificationCenter } } - public void updatePrintingStrings() { + private String getUserNameForTyping(TLRPC.User user) { + if (user == null) { + return ""; + } + if (user.first_name != null && user.first_name.length() > 0) { + return user.first_name; + } else if (user.last_name != null && user.last_name.length() > 0) { + return user.last_name; + } + return ""; + } + + private void updatePrintingStrings() { final HashMap newPrintingStrings = new HashMap<>(); + final HashMap newPrintingStringsTypes = new HashMap<>(); ArrayList keys = new ArrayList<>(printingUsers.keySet()); - for (Long key : keys) { - if (key > 0 || key.intValue() == 0) { - newPrintingStrings.put(key, LocaleController.getString("Typing", R.string.Typing)); + for (HashMap.Entry> entry : printingUsers.entrySet()) { + long key = entry.getKey(); + ArrayList arr = entry.getValue(); + + int lower_id = (int) key; + + if (lower_id > 0 || lower_id == 0 || arr.size() == 1) { + PrintingUser pu = arr.get(0); + TLRPC.User user = getUser(pu.userId); + if (user == null) { + return; + } + if (pu.action instanceof TLRPC.TL_sendMessageUploadAudioAction || pu.action instanceof TLRPC.TL_sendMessageRecordAudioAction) { + if (lower_id < 0) { + newPrintingStrings.put(key, LocaleController.formatString("IsRecordingAudio", R.string.IsRecordingAudio, getUserNameForTyping(user))); + } else { + newPrintingStrings.put(key, LocaleController.getString("RecordingAudio", R.string.RecordingAudio)); + } + newPrintingStringsTypes.put(key, 1); + } else if (pu.action instanceof TLRPC.TL_sendMessageUploadVideoAction || pu.action instanceof TLRPC.TL_sendMessageRecordVideoAction) { + if (lower_id < 0) { + newPrintingStrings.put(key, LocaleController.formatString("IsSendingVideo", R.string.IsSendingVideo, getUserNameForTyping(user))); + } else { + newPrintingStrings.put(key, LocaleController.getString("SendingVideoStatus", R.string.SendingVideoStatus)); + } + newPrintingStringsTypes.put(key, 2); + } else if (pu.action instanceof TLRPC.TL_sendMessageUploadDocumentAction) { + if (lower_id < 0) { + newPrintingStrings.put(key, LocaleController.formatString("IsSendingFile", R.string.IsSendingFile, getUserNameForTyping(user))); + } else { + newPrintingStrings.put(key, LocaleController.getString("SendingFile", R.string.SendingFile)); + } + newPrintingStringsTypes.put(key, 2); + } else if (pu.action instanceof TLRPC.TL_sendMessageUploadPhotoAction) { + if (lower_id < 0) { + newPrintingStrings.put(key, LocaleController.formatString("IsSendingPhoto", R.string.IsSendingPhoto, getUserNameForTyping(user))); + } else { + newPrintingStrings.put(key, LocaleController.getString("SendingPhoto", R.string.SendingPhoto)); + } + newPrintingStringsTypes.put(key, 2); + } else { + if (lower_id < 0) { + newPrintingStrings.put(key, String.format("%s %s", getUserNameForTyping(user), LocaleController.getString("IsTyping", R.string.IsTyping))); + } else { + newPrintingStrings.put(key, LocaleController.getString("Typing", R.string.Typing)); + } + newPrintingStringsTypes.put(key, 0); + } } else { - ArrayList arr = printingUsers.get(key); int count = 0; String label = ""; for (PrintingUser pu : arr) { @@ -1292,11 +1363,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter if (label.length() != 0) { label += ", "; } - if (user.first_name != null && user.first_name.length() > 0) { - label += user.first_name; - } else if (user.last_name != null && user.last_name.length() > 0) { - label += user.last_name; - } + label += getUserNameForTyping(user); count++; } if (count == 2) { @@ -1304,15 +1371,12 @@ public class MessagesController implements NotificationCenter.NotificationCenter } } if (label.length() != 0) { - if (count > 1) { - if (arr.size() > 2) { - newPrintingStrings.put(key, String.format("%s %s", label, LocaleController.formatPluralString("AndMoreTyping", arr.size() - 2))); - } else { - newPrintingStrings.put(key, String.format("%s %s", label, LocaleController.getString("AreTyping", R.string.AreTyping))); - } + if (arr.size() > 2) { + newPrintingStrings.put(key, String.format("%s %s", label, LocaleController.formatPluralString("AndMoreTyping", arr.size() - 2))); } else { - newPrintingStrings.put(key, String.format("%s %s", label, LocaleController.getString("IsTyping", R.string.IsTyping))); + newPrintingStrings.put(key, String.format("%s %s", label, LocaleController.getString("AreTyping", R.string.AreTyping))); } + newPrintingStringsTypes.put(key, 0); } } } @@ -1323,23 +1387,32 @@ public class MessagesController implements NotificationCenter.NotificationCenter @Override public void run() { printingStrings = newPrintingStrings; + printingStringsTypes = newPrintingStringsTypes; } }); } - public void cancelTyping(long dialog_id) { - sendingTypings.remove(dialog_id); + public void cancelTyping(int action, long dialog_id) { + HashMap typings = sendingTypings.get(action); + if (typings != null) { + typings.remove(dialog_id); + } } - public void sendTyping(final long dialog_id, int classGuid) { + public void sendTyping(final long dialog_id, final int action, int classGuid) { if (dialog_id == 0) { return; } - if (sendingTypings.get(dialog_id) != null) { + HashMap typings = sendingTypings.get(action); + if (typings != null && typings.get(dialog_id) != null) { return; } - int lower_part = (int)dialog_id; - int high_id = (int)(dialog_id >> 32); + if (typings == null) { + typings = new HashMap<>(); + sendingTypings.put(action, typings); + } + int lower_part = (int) dialog_id; + int high_id = (int) (dialog_id >> 32); if (lower_part != 0) { if (high_id == 1) { return; @@ -1364,21 +1437,41 @@ public class MessagesController implements NotificationCenter.NotificationCenter return; } } - req.action = new TLRPC.TL_sendMessageTypingAction(); - sendingTypings.put(dialog_id, true); + if (action == 0) { + req.action = new TLRPC.TL_sendMessageTypingAction(); + } else if (action == 1) { + req.action = new TLRPC.TL_sendMessageRecordAudioAction(); + } else if (action == 2) { + req.action = new TLRPC.TL_sendMessageCancelAction(); + } else if (action == 3) { + req.action = new TLRPC.TL_sendMessageUploadDocumentAction(); + } else if (action == 4) { + req.action = new TLRPC.TL_sendMessageUploadPhotoAction(); + } else if (action == 5) { + req.action = new TLRPC.TL_sendMessageUploadVideoAction(); + } + typings.put(dialog_id, true); long reqId = ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() { @Override public void run(TLObject response, TLRPC.TL_error error) { AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { - sendingTypings.remove(dialog_id); + HashMap typings = sendingTypings.get(action); + if (typings != null) { + typings.remove(dialog_id); + } } }); } }, true, RPCRequest.RPCRequestClassGeneric | RPCRequest.RPCRequestClassFailOnServerErrors); - ConnectionsManager.getInstance().bindRequestToGuid(reqId, classGuid); + if (classGuid != 0) { + ConnectionsManager.getInstance().bindRequestToGuid(reqId, classGuid); + } } else { + if (action != 0) { + return; + } TLRPC.EncryptedChat chat = getEncryptedChat(high_id); if (chat.auth_key != null && chat.auth_key.length > 1 && chat instanceof TLRPC.TL_encryptedChat) { TLRPC.TL_messages_setEncryptedTyping req = new TLRPC.TL_messages_setEncryptedTyping(); @@ -1386,20 +1479,30 @@ public class MessagesController implements NotificationCenter.NotificationCenter req.peer.chat_id = chat.id; req.peer.access_hash = chat.access_hash; req.typing = true; - sendingTypings.put(dialog_id, true); + typings.put(dialog_id, true); long reqId = ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() { @Override public void run(TLObject response, TLRPC.TL_error error) { - sendingTypings.remove(dialog_id); + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + HashMap typings = sendingTypings.get(action); + if (typings != null) { + typings.remove(dialog_id); + } + } + }); } }, true, RPCRequest.RPCRequestClassGeneric | RPCRequest.RPCRequestClassFailOnServerErrors); - ConnectionsManager.getInstance().bindRequestToGuid(reqId, classGuid); + if (classGuid != 0) { + ConnectionsManager.getInstance().bindRequestToGuid(reqId, classGuid); + } } } } public void loadMessages(final long dialog_id, final int count, final int max_id, boolean fromCache, int midDate, final int classGuid, final int load_type, final int last_message_id, final int first_message_id, final boolean allowCache) { - int lower_part = (int)dialog_id; + int lower_part = (int) dialog_id; if (fromCache || lower_part == 0) { MessagesStorage.getInstance().getMessages(dialog_id, count, max_id, midDate, classGuid, load_type); } else { @@ -1409,6 +1512,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter req.peer.chat_id = -lower_part; } else { TLRPC.User user = getUser(lower_part); + if (user == null) { + return; + } if (user instanceof TLRPC.TL_userForeign || user instanceof TLRPC.TL_userRequest) { req.peer = new TLRPC.TL_inputPeerForeign(); req.peer.user_id = user.id; @@ -1567,7 +1673,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter currentDialog.unread_count = entry.getValue(); } } - NotificationCenter.getInstance().postNotificationName(NotificationCenter.dialogsNeedReload); + NotificationCenter.getInstance().postNotificationName(NotificationCenter.updateInterfaces, UPDATE_MASK_READ_DIALOG_MESSAGE); NotificationsController.getInstance().processDialogsUpdateRead(dialogsToUpdate); } }); @@ -1679,7 +1785,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { - putUsers(dialogsRes.users, isCache); + putUsers(dialogsRes.users, true); loadingDialogs = false; if (resetEnd) { dialogsEndReached = false; @@ -1803,18 +1909,35 @@ public class MessagesController implements NotificationCenter.NotificationCenter dialogsEndReached = (dialogsRes.dialogs.size() == 0 || dialogsRes.dialogs.size() != count) && !isCache; NotificationCenter.getInstance().postNotificationName(NotificationCenter.dialogsNeedReload); + generateUpdateMessage(); } }); } }); } + public void markMessageContentAsRead(int mid) { + TLRPC.TL_messages_readMessageContents req = new TLRPC.TL_messages_readMessageContents(); + req.id.add(mid); + MessagesStorage.getInstance().markMessagesContentAsRead(req.id); + NotificationCenter.getInstance().postNotificationName(NotificationCenter.messagesReadContent, req.id); + ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() { + @Override + public void run(TLObject response, TLRPC.TL_error error) { + if (error == null) { + TLRPC.TL_messages_affectedMessages res = (TLRPC.TL_messages_affectedMessages) response; + processNewDifferenceParams(-1, res.pts, -1, res.pts_count); + } + } + }); + } + public void markMessageAsRead(final long dialog_id, final long random_id, int ttl) { if (random_id == 0 || dialog_id == 0 || ttl <= 0) { return; } - int lower_part = (int)dialog_id; - int high_id = (int)(dialog_id >> 32); + int lower_part = (int) dialog_id; + int high_id = (int) (dialog_id >> 32); if (lower_part != 0) { return; } @@ -1830,8 +1953,8 @@ public class MessagesController implements NotificationCenter.NotificationCenter } public void markDialogAsRead(final long dialog_id, final int max_id, final int max_positive_id, final int offset, final int max_date, final boolean was, final boolean popup) { - int lower_part = (int)dialog_id; - int high_id = (int)(dialog_id >> 32); + int lower_part = (int) dialog_id; + int high_id = (int) (dialog_id >> 32); if (lower_part != 0) { if (max_positive_id == 0 && offset == 0 || high_id == 1) { @@ -1956,7 +2079,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter chat.title = title; chat.photo = new TLRPC.TL_chatPhotoEmpty(); chat.participants_count = selectedContacts.size(); - chat.date = (int)(System.currentTimeMillis() / 1000); + chat.date = (int) (System.currentTimeMillis() / 1000); chat.left = false; chat.version = 1; UserConfig.lastBroadcastId--; @@ -1973,7 +2096,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter TLRPC.TL_chatParticipant participant = new TLRPC.TL_chatParticipant(); participant.user_id = id; participant.inviter_id = UserConfig.getClientUserId(); - participant.date = (int)(System.currentTimeMillis() / 1000); + participant.date = (int) (System.currentTimeMillis() / 1000); participants.participants.add(participant); } MessagesStorage.getInstance().updateChatInfo(chat.id, participants, false); @@ -2030,7 +2153,6 @@ public class MessagesController implements NotificationCenter.NotificationCenter public void run() { putUsers(updates.users, false); putChats(updates.chats, false); - TLRPC.Chat chat = null; if (updates.chats != null && !updates.chats.isEmpty()) { NotificationCenter.getInstance().postNotificationName(NotificationCenter.chatDidCreated, updates.chats.get(0).id); } else { @@ -2124,20 +2246,18 @@ public class MessagesController implements NotificationCenter.NotificationCenter MessagesStorage.getInstance().putUsersAndChats(null, chatArrayList, true, true); boolean changed = false; - if (info != null) { - for (int a = 0; a < info.participants.size(); a++) { - TLRPC.TL_chatParticipant p = info.participants.get(a); - if (p.user_id == user.id) { - info.participants.remove(a); - changed = true; - break; - } - } - if (changed) { - MessagesStorage.getInstance().updateChatInfo(info.chat_id, info, true); - NotificationCenter.getInstance().postNotificationName(NotificationCenter.chatInfoDidLoaded, info.chat_id, info); + for (int a = 0; a < info.participants.size(); a++) { + TLRPC.TL_chatParticipant p = info.participants.get(a); + if (p.user_id == user.id) { + info.participants.remove(a); + changed = true; + break; } } + if (changed) { + MessagesStorage.getInstance().updateChatInfo(info.chat_id, info, true); + NotificationCenter.getInstance().postNotificationName(NotificationCenter.chatInfoDidLoaded, info.chat_id, info); + } NotificationCenter.getInstance().postNotificationName(NotificationCenter.updateInterfaces, UPDATE_MASK_CHAT_MEMBERS); } } @@ -2203,12 +2323,57 @@ public class MessagesController implements NotificationCenter.NotificationCenter } } - public void logOut() { - TLRPC.TL_auth_logOut req = new TLRPC.TL_auth_logOut(); - ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() { + public void performLogout(boolean byUser) { + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); + SharedPreferences.Editor editor = preferences.edit(); + editor.clear().commit(); + if (byUser) { + unregistedPush(); + TLRPC.TL_auth_logOut req = new TLRPC.TL_auth_logOut(); + ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() { + @Override + public void run(TLObject response, TLRPC.TL_error error) { + ConnectionsManager.getInstance().cleanUp(); + } + }); + } else { + ConnectionsManager.getInstance().cleanUp(); + } + UserConfig.clearConfig(); + NotificationCenter.getInstance().postNotificationName(NotificationCenter.appDidLogout); + MessagesStorage.getInstance().cleanUp(false); + cleanUp(); + ContactsController.getInstance().deleteAllAppAccounts(); + } + + public void generateUpdateMessage() { + Utilities.stageQueue.postRunnable(new Runnable() { @Override - public void run(TLObject response, TLRPC.TL_error error) { - ConnectionsManager.getInstance().cleanUp(); + public void run() { + try { + String build = LocaleController.getString("updateBuild", R.string.updateBuild); + if (build != null) { + int version = Utilities.parseInt(build); + if (version == 0) { + version = 524; + } + if (version <= UserConfig.lastUpdateVersion) { + return; + } + UserConfig.lastUpdateVersion = version; + UserConfig.saveConfig(false); + } + TLRPC.TL_updateServiceNotification update = new TLRPC.TL_updateServiceNotification(); + update.message = LocaleController.getString("updateText", R.string.updateText); + update.media = new TLRPC.TL_messageMediaEmpty(); + update.type = "update"; + update.popup = false; + ArrayList updates = new ArrayList<>(); + updates.add(update); + processUpdateArray(updates, null, null); + } catch (Exception e) { + FileLog.e("tmessages", e); + } } }); } @@ -2226,18 +2391,14 @@ public class MessagesController implements NotificationCenter.NotificationCenter req.token = regid; req.app_sandbox = false; try { - req.lang_code = LocaleController.getLocaleString(Locale.getDefault()); - req.device_model = Build.MANUFACTURER + Build.MODEL; - if (req.device_model == null) { - req.device_model = "Android unknown"; + req.lang_code = LocaleController.getLocaleString(LocaleController.getInstance().getSystemDefaultLocale()); + if (req.lang_code.length() == 0) { + req.lang_code = "en"; } + req.device_model = Build.MANUFACTURER + Build.MODEL; req.system_version = "SDK " + Build.VERSION.SDK_INT; PackageInfo pInfo = ApplicationLoader.applicationContext.getPackageManager().getPackageInfo(ApplicationLoader.applicationContext.getPackageName(), 0); req.app_version = pInfo.versionName + " (" + pInfo.versionCode + ")"; - if (req.app_version == null) { - req.app_version = "App version unknown"; - } - } catch (Exception e) { FileLog.e("tmessages", e); req.lang_code = "en"; @@ -2351,15 +2512,15 @@ public class MessagesController implements NotificationCenter.NotificationCenter } else if (type == 1) { if (updates.pts <= MessagesStorage.lastPtsValue) { return 2; - } else if (MessagesStorage.lastPtsValue + updates.pts_count == updates.pts) { + } else if (MessagesStorage.lastPtsValue + updates.pts_count == updates.pts) { return 0; } else { return 1; } } else if (type == 2) { - if (updates.qts <= MessagesStorage.lastQtsValue) { + if (updates.pts <= MessagesStorage.lastQtsValue) { return 2; - } else if (MessagesStorage.lastQtsValue + 1 == updates.qts) { + } else if (MessagesStorage.lastQtsValue + updates.updates.size() == updates.pts) { return 0; } else { return 1; @@ -2375,14 +2536,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter Collections.sort(updatesQueue, new Comparator() { @Override public int compare(TLRPC.Updates updates, TLRPC.Updates updates2) { - int seq1 = getUpdateSeq(updates); - int seq2 = getUpdateSeq(updates2); - if (seq1 == seq2) { - return 0; - } else if (seq1 > seq2) { - return 1; - } - return -1; + return AndroidUtilities.compare(getUpdateSeq(updates), getUpdateSeq(updates2)); } }); } else if (type == 1) { @@ -2390,12 +2544,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter Collections.sort(updatesQueue, new Comparator() { @Override public int compare(TLRPC.Updates updates, TLRPC.Updates updates2) { - if (updates.pts == updates2.pts) { - return 0; - } else if (updates.pts > updates2.pts) { - return 1; - } - return -1; + return AndroidUtilities.compare(updates.pts, updates2.pts); } }); } else if (type == 2) { @@ -2403,16 +2552,11 @@ public class MessagesController implements NotificationCenter.NotificationCenter Collections.sort(updatesQueue, new Comparator() { @Override public int compare(TLRPC.Updates updates, TLRPC.Updates updates2) { - if (updates.qts == updates2.qts) { - return 0; - } else if (updates.qts > updates2.qts) { - return 1; - } - return -1; + return AndroidUtilities.compare(updates.pts, updates2.pts); } }); } - if (!updatesQueue.isEmpty()) { + if (updatesQueue != null && !updatesQueue.isEmpty()) { boolean anyProceed = false; if (state == 2) { TLRPC.Updates updates = updatesQueue.get(0); @@ -2420,8 +2564,8 @@ public class MessagesController implements NotificationCenter.NotificationCenter MessagesStorage.lastSeqValue = getUpdateSeq(updates); } else if (type == 1) { MessagesStorage.lastPtsValue = updates.pts; - } else if (type == 2) { - MessagesStorage.lastQtsValue = updates.qts; + } else { + MessagesStorage.lastQtsValue = updates.pts; } } for (int a = 0; a < updatesQueue.size(); a++) { @@ -2570,15 +2714,6 @@ public class MessagesController implements NotificationCenter.NotificationCenter for (TLRPC.Message message : res.new_messages) { MessageObject obj = new MessageObject(message, usersDict, true); - long dialog_id = obj.messageOwner.dialog_id; - if (dialog_id == 0) { - if (obj.messageOwner.to_id.chat_id != 0) { - dialog_id = -obj.messageOwner.to_id.chat_id; - } else { - dialog_id = obj.messageOwner.to_id.user_id; - } - } - if (!obj.isOut() && obj.isUnread()) { pushMessages.add(obj); } @@ -2636,7 +2771,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter SecretChatHelper.getInstance().processPendingEncMessages(); } - if (res != null && !res.other_updates.isEmpty()) { + if (!res.other_updates.isEmpty()) { processUpdateArray(res.other_updates, res.users, res.chats); } @@ -2710,6 +2845,17 @@ public class MessagesController implements NotificationCenter.NotificationCenter }); } + private int getUpdateType(TLRPC.Update update) { + if (update instanceof TLRPC.TL_updateNewMessage || update instanceof TLRPC.TL_updateReadMessagesContents || update instanceof TLRPC.TL_updateReadHistoryInbox || + update instanceof TLRPC.TL_updateReadHistoryOutbox || update instanceof TLRPC.TL_updateDeleteMessages) { + return 0; + } else if (update instanceof TLRPC.TL_updateNewEncryptedMessage) { + return 1; + } else { + return 2; + } + } + public void processUpdates(final TLRPC.Updates updates, boolean fromQueue) { boolean needGetDiff = false; boolean needReceivedQueue = false; @@ -2719,11 +2865,12 @@ public class MessagesController implements NotificationCenter.NotificationCenter arr.add(updates.update); processUpdateArray(arr, null, null); } else if (updates instanceof TLRPC.TL_updateShortChatMessage || updates instanceof TLRPC.TL_updateShortMessage) { - TLRPC.User user = getUser(updates.user_id); + final int user_id = updates instanceof TLRPC.TL_updateShortChatMessage ? updates.from_id : updates.user_id; + TLRPC.User user = getUser(user_id); TLRPC.User user2 = null; if (user == null) { - user = MessagesStorage.getInstance().getUserSync(updates.user_id); + user = MessagesStorage.getInstance().getUserSync(user_id); putUser(user, true); } @@ -2737,7 +2884,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter needFwdUser = true; } - boolean missingData = false; + boolean missingData; if (updates instanceof TLRPC.TL_updateShortMessage) { missingData = user == null || needFwdUser && user2 == null; } else { @@ -2763,13 +2910,13 @@ public class MessagesController implements NotificationCenter.NotificationCenter if ((updates.flags & TLRPC.MESSAGE_FLAG_OUT) != 0) { message.from_id = UserConfig.getClientUserId(); } else { - message.from_id = updates.user_id; + message.from_id = user_id; } message.to_id = new TLRPC.TL_peerUser(); - message.to_id.user_id = updates.user_id; - message.dialog_id = updates.user_id; + message.to_id.user_id = user_id; + message.dialog_id = user_id; } else { - message.from_id = updates.user_id; + message.from_id = user_id; message.to_id = new TLRPC.TL_peerChat(); message.to_id.chat_id = updates.chat_id; message.dialog_id = -updates.chat_id; @@ -2798,7 +2945,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter if (printUpdate) { NotificationCenter.getInstance().postNotificationName(NotificationCenter.updateInterfaces, UPDATE_MASK_USER_PRINT); } - updateInterfaceWithMessages(updates.user_id, objArr); + updateInterfaceWithMessages(user_id, objArr); NotificationCenter.getInstance().postNotificationName(NotificationCenter.dialogsNeedReload); } }); @@ -2836,11 +2983,11 @@ public class MessagesController implements NotificationCenter.NotificationCenter MessagesStorage.getInstance().putMessages(arr, false, true, false, 0); } else if (MessagesStorage.lastPtsValue != updates.pts) { FileLog.e("tmessages", "need get diff short message, pts: " + MessagesStorage.lastPtsValue + " " + updates.pts + " count = " + updates.pts_count); - if (gettingDifference || updatesStartWaitTimePts == 0 || updatesStartWaitTimePts != 0 && updatesStartWaitTimePts + 1500 > System.currentTimeMillis()) { + if (gettingDifference || updatesStartWaitTimePts == 0 || updatesStartWaitTimePts + 1500 > System.currentTimeMillis()) { if (updatesStartWaitTimePts == 0) { updatesStartWaitTimePts = System.currentTimeMillis(); } - FileLog.e("tmessages", "add short message to queue"); + FileLog.e("tmessages", "add to queue"); updatesQueuePts.add(updates); } else { needGetDiff = true; @@ -2849,62 +2996,98 @@ public class MessagesController implements NotificationCenter.NotificationCenter } } else if (updates instanceof TLRPC.TL_updatesCombined || updates instanceof TLRPC.TL_updates) { MessagesStorage.getInstance().putUsersAndChats(updates.users, updates.chats, true, true); - int lastQtsValue = MessagesStorage.lastQtsValue; + Collections.sort(updates.updates, new Comparator() { + @Override + public int compare(TLRPC.Update lhs, TLRPC.Update rhs) { + int ltype = getUpdateType(lhs); + int rtype = getUpdateType(rhs); + if (ltype != rtype) { + return AndroidUtilities.compare(ltype, rtype); + } else if (ltype == 0) { + return AndroidUtilities.compare(lhs.pts, rhs.pts); + } else if (ltype == 1) { + return AndroidUtilities.compare(lhs.qts, rhs.qts); + } + return 0; + } + }); for (int a = 0; a < updates.updates.size(); a++) { TLRPC.Update update = updates.updates.get(a); - if (update instanceof TLRPC.TL_updateNewMessage || update instanceof TLRPC.TL_updateReadMessages || update instanceof TLRPC.TL_updateReadHistoryInbox || - update instanceof TLRPC.TL_updateReadHistoryOutbox || update instanceof TLRPC.TL_updateDeleteMessages) { + if (getUpdateType(update) == 0) { TLRPC.TL_updates updatesNew = new TLRPC.TL_updates(); updatesNew.updates.add(update); updatesNew.pts = update.pts; updatesNew.pts_count = update.pts_count; - if (MessagesStorage.lastPtsValue + update.pts_count == update.pts) { + for (int b = a + 1; b < updates.updates.size(); b++) { + TLRPC.Update update2 = updates.updates.get(b); + if (getUpdateType(update2) == 0 && updatesNew.pts + update2.pts_count == update2.pts) { + updatesNew.updates.add(update2); + updatesNew.pts = update2.pts; + updatesNew.pts_count += update2.pts_count; + updates.updates.remove(b); + b--; + } else { + break; + } + } + if (MessagesStorage.lastPtsValue + updatesNew.pts_count == updatesNew.pts) { if (!processUpdateArray(updatesNew.updates, updates.users, updates.chats)) { FileLog.e("tmessages", "need get diff inner TL_updates, seq: " + MessagesStorage.lastSeqValue + " " + updates.seq); needGetDiff = true; } else { - MessagesStorage.lastPtsValue = update.pts; + MessagesStorage.lastPtsValue = updatesNew.pts; } - } else if (MessagesStorage.lastPtsValue != update.pts) { - FileLog.e("tmessages", update + " need get diff, pts: " + MessagesStorage.lastPtsValue + " " + update.pts + " count = " + update.pts_count); + } else if (MessagesStorage.lastPtsValue != updatesNew.pts) { + FileLog.e("tmessages", update + " need get diff, pts: " + MessagesStorage.lastPtsValue + " " + updatesNew.pts + " count = " + updatesNew.pts_count); if (gettingDifference || updatesStartWaitTimePts == 0 || updatesStartWaitTimePts != 0 && updatesStartWaitTimePts + 1500 > System.currentTimeMillis()) { if (updatesStartWaitTimePts == 0) { updatesStartWaitTimePts = System.currentTimeMillis(); } - FileLog.e("tmessages", "add short message to queue"); + FileLog.e("tmessages", "add to queue"); updatesQueuePts.add(updatesNew); } else { needGetDiff = true; } } - } else if (update instanceof TLRPC.TL_updateNewEncryptedMessage) { + } else if (getUpdateType(update) == 1) { TLRPC.TL_updates updatesNew = new TLRPC.TL_updates(); updatesNew.updates.add(update); - updatesNew.qts = update.qts; - if (MessagesStorage.lastQtsValue == 0 || MessagesStorage.lastQtsValue + 1 == update.qts) { + updatesNew.pts = update.qts; + for (int b = a + 1; b < updates.updates.size(); b++) { + TLRPC.Update update2 = updates.updates.get(b); + if (getUpdateType(update2) == 1 && updatesNew.pts + 1 == update2.qts) { + updatesNew.updates.add(update2); + updatesNew.pts = update2.qts; + updates.updates.remove(b); + b--; + } else { + break; + } + } + if (MessagesStorage.lastQtsValue == 0 || MessagesStorage.lastQtsValue + updatesNew.updates.size() == updatesNew.pts) { processUpdateArray(updatesNew.updates, updates.users, updates.chats); - MessagesStorage.lastQtsValue = update.qts; + MessagesStorage.lastQtsValue = updatesNew.pts; needReceivedQueue = true; - } else if (MessagesStorage.lastPtsValue != update.qts) { - FileLog.e("tmessages", update + " need get diff, qts: " + MessagesStorage.lastQtsValue + " " + update.qts); + } else if (MessagesStorage.lastPtsValue != updatesNew.pts) { + FileLog.e("tmessages", update + " need get diff, qts: " + MessagesStorage.lastQtsValue + " " + updatesNew.pts); if (gettingDifference || updatesStartWaitTimeQts == 0 || updatesStartWaitTimeQts != 0 && updatesStartWaitTimeQts + 1500 > System.currentTimeMillis()) { if (updatesStartWaitTimeQts == 0) { updatesStartWaitTimeQts = System.currentTimeMillis(); } - FileLog.e("tmessages", "add short message to queue"); + FileLog.e("tmessages", "add to queue"); updatesQueueQts.add(updatesNew); } else { needGetDiff = true; } } } else { - continue; + break; } updates.updates.remove(a); a--; } - boolean processUpdate = false; + boolean processUpdate; if (updates instanceof TLRPC.TL_updatesCombined) { processUpdate = MessagesStorage.lastSeqValue + 1 == updates.seq_start || MessagesStorage.lastSeqValue == updates.seq_start; } else { @@ -2925,7 +3108,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter FileLog.e("tmessages", "need get diff TL_updates, seq: " + MessagesStorage.lastSeqValue + " " + updates.seq); } - if (gettingDifference || updatesStartWaitTimeSeq == 0 || updatesStartWaitTimeSeq != 0 && updatesStartWaitTimeSeq + 1500 > System.currentTimeMillis()) { + if (gettingDifference || updatesStartWaitTimeSeq == 0 || updatesStartWaitTimeSeq + 1500 > System.currentTimeMillis()) { if (updatesStartWaitTimeSeq == 0) { updatesStartWaitTimeSeq = System.currentTimeMillis(); } @@ -2957,7 +3140,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter } else if (a == 2) { updatesQueue = updatesQueueQts; } - if (!updatesQueue.isEmpty()) { + if (updatesQueue != null && !updatesQueue.isEmpty()) { processUpdatesQueue(a, 0); } } @@ -2996,6 +3179,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter final ArrayList messagesArr = new ArrayList<>(); final HashMap markAsReadMessagesInbox = new HashMap<>(); final HashMap markAsReadMessagesOutbox = new HashMap<>(); + final ArrayList markAsReadMessages = new ArrayList<>(); final HashMap markAsReadEncrypted = new HashMap<>(); final ArrayList deletedMessages = new ArrayList<>(); boolean printChanged = false; @@ -3040,7 +3224,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter for (TLRPC.Update update : updates) { if (update instanceof TLRPC.TL_updateNewMessage) { - TLRPC.TL_updateNewMessage upd = (TLRPC.TL_updateNewMessage)update; + TLRPC.TL_updateNewMessage upd = (TLRPC.TL_updateNewMessage) update; if (checkForUsers) { TLRPC.User user = getUser(upd.message.from_id); if (usersDict.get(upd.message.from_id) == null && user == null || upd.message.to_id.chat_id != 0 && chatsDict.get(upd.message.to_id.chat_id) == null && getChat(upd.message.to_id.chat_id) == null) { @@ -3078,8 +3262,8 @@ public class MessagesController implements NotificationCenter.NotificationCenter if (!obj.isOut() && obj.isUnread()) { pushMessages.add(obj); } - } else if (update instanceof TLRPC.TL_updateReadMessages) { - //markAsReadMessages.addAll(update.messages); disabled for now + } else if (update instanceof TLRPC.TL_updateReadMessagesContents) { + markAsReadMessages.addAll(update.messages); } else if (update instanceof TLRPC.TL_updateReadHistoryInbox) { TLRPC.Peer peer = ((TLRPC.TL_updateReadHistoryInbox) update).peer; if (peer.chat_id != 0) { @@ -3097,30 +3281,48 @@ public class MessagesController implements NotificationCenter.NotificationCenter } else if (update instanceof TLRPC.TL_updateDeleteMessages) { deletedMessages.addAll(update.messages); } else if (update instanceof TLRPC.TL_updateUserTyping || update instanceof TLRPC.TL_updateChatUserTyping) { - if (update.action instanceof TLRPC.TL_sendMessageTypingAction && update.user_id != UserConfig.getClientUserId()) { + if (update.user_id != UserConfig.getClientUserId()) { long uid = -update.chat_id; if (uid == 0) { uid = update.user_id; } ArrayList arr = printingUsers.get(uid); - if (arr == null) { - arr = new ArrayList<>(); - printingUsers.put(uid, arr); - } - boolean exist = false; - for (PrintingUser u : arr) { - if (u.userId == update.user_id) { - exist = true; - u.lastTime = currentTime; - break; + if (update.action instanceof TLRPC.TL_sendMessageCancelAction) { + if (arr != null) { + for (int a = 0; a < arr.size(); a++) { + PrintingUser pu = arr.get(a); + if (pu.userId == update.user_id) { + arr.remove(a); + printChanged = true; + break; + } + } + if (arr.isEmpty()) { + printingUsers.remove(uid); + } + } + } else { + if (arr == null) { + arr = new ArrayList<>(); + printingUsers.put(uid, arr); + } + boolean exist = false; + for (PrintingUser u : arr) { + if (u.userId == update.user_id) { + exist = true; + u.lastTime = currentTime; + u.action = update.action; + break; + } + } + if (!exist) { + PrintingUser newUser = new PrintingUser(); + newUser.userId = update.user_id; + newUser.lastTime = currentTime; + newUser.action = update.action; + arr.add(newUser); + printChanged = true; } - } - if (!exist) { - PrintingUser newUser = new PrintingUser(); - newUser.userId = update.user_id; - newUser.lastTime = currentTime; - arr.add(newUser); - printChanged = true; } onlinePrivacy.put(update.user_id, ConnectionsManager.getInstance().getCurrentTime()); } @@ -3181,8 +3383,6 @@ public class MessagesController implements NotificationCenter.NotificationCenter contactsIds.add(-update.user_id); } } - } else if (update instanceof TLRPC.TL_updateActivation) { - //DEPRECATED } else if (update instanceof TLRPC.TL_updateNewAuthorization) { AndroidUtilities.runOnUIThread(new Runnable() { @Override @@ -3217,7 +3417,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter } else if (update instanceof TLRPC.TL_updateNewEncryptedMessage) { ArrayList decryptedMessages = SecretChatHelper.getInstance().decryptMessage(((TLRPC.TL_updateNewEncryptedMessage) update).message); if (decryptedMessages != null && !decryptedMessages.isEmpty()) { - int cid = ((TLRPC.TL_updateNewEncryptedMessage)update).message.chat_id; + int cid = ((TLRPC.TL_updateNewEncryptedMessage) update).message.chat_id; long uid = ((long) cid) << 32; ArrayList arr = messages.get(uid); if (arr == null) { @@ -3247,6 +3447,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter if (u.userId == update.user_id) { exist = true; u.lastTime = currentTime; + u.action = new TLRPC.TL_sendMessageTypingAction(); break; } } @@ -3254,6 +3455,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter PrintingUser newUser = new PrintingUser(); newUser.userId = update.user_id; newUser.lastTime = currentTime; + newUser.action = new TLRPC.TL_sendMessageTypingAction(); arr.add(newUser); printChanged = true; } @@ -3271,7 +3473,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter } else if (update instanceof TLRPC.TL_updateEncryption) { SecretChatHelper.getInstance().processUpdateEncryption((TLRPC.TL_updateEncryption) update, usersDict); } else if (update instanceof TLRPC.TL_updateUserBlocked) { - final TLRPC.TL_updateUserBlocked finalUpdate = (TLRPC.TL_updateUserBlocked)update; + final TLRPC.TL_updateUserBlocked finalUpdate = (TLRPC.TL_updateUserBlocked) update; if (finalUpdate.blocked) { ArrayList ids = new ArrayList<>(); ids.add(finalUpdate.user_id); @@ -3304,13 +3506,13 @@ public class MessagesController implements NotificationCenter.NotificationCenter newMessage.local_id = newMessage.id = UserConfig.getNewMessageId(); UserConfig.saveConfig(false); newMessage.flags = TLRPC.MESSAGE_FLAG_UNREAD; - newMessage.date = update.date; + newMessage.date = ConnectionsManager.getInstance().getCurrentTime(); newMessage.from_id = 777000; newMessage.to_id = new TLRPC.TL_peerUser(); newMessage.to_id.user_id = UserConfig.getClientUserId(); newMessage.dialog_id = 777000; newMessage.media = update.media; - newMessage.message = ((TLRPC.TL_updateServiceNotification)update).message; + newMessage.message = ((TLRPC.TL_updateServiceNotification) update).message; messagesArr.add(newMessage); MessageObject obj = new MessageObject(newMessage, usersDict, true); @@ -3371,7 +3573,6 @@ public class MessagesController implements NotificationCenter.NotificationCenter public void run() { int updateMask = interfaceUpdateMaskFinal; - boolean avatarsUpdate = false; if (!updatesOnMainThread.isEmpty()) { ArrayList dbUsers = new ArrayList<>(); ArrayList dbUsersStatus = new ArrayList<>(); @@ -3403,14 +3604,16 @@ public class MessagesController implements NotificationCenter.NotificationCenter } } else if (update instanceof TLRPC.TL_updateUserName) { if (currentUser != null) { + if (!(currentUser instanceof TLRPC.TL_userContact)) { + currentUser.first_name = update.first_name; + currentUser.last_name = update.last_name; + } if (currentUser.username != null && currentUser.username.length() > 0) { usersByUsernames.remove(currentUser.username); } if (update.username != null && update.username.length() > 0) { usersByUsernames.put(update.username, currentUser); } - currentUser.first_name = update.first_name; - currentUser.last_name = update.last_name; currentUser.username = update.username; } toDbUser.first_name = update.first_name; @@ -3421,13 +3624,12 @@ public class MessagesController implements NotificationCenter.NotificationCenter if (currentUser != null) { currentUser.photo = update.photo; } - avatarsUpdate = true; toDbUser.photo = update.photo; dbUsers.add(toDbUser); } else if (update instanceof TLRPC.TL_updateUserPhone) { if (currentUser != null) { currentUser.phone = update.phone; - Utilities.photoBookQueue.postRunnable(new Runnable() { + Utilities.phoneBookQueue.postRunnable(new Runnable() { @Override public void run() { ContactsController.getInstance().addContactToPhoneBook(currentUser, true); @@ -3437,14 +3639,15 @@ public class MessagesController implements NotificationCenter.NotificationCenter toDbUser.phone = update.phone; dbUsers.add(toDbUser); } else if (update instanceof TLRPC.TL_updateNotifySettings) { - if (update.notify_settings instanceof TLRPC.TL_peerNotifySettings && update.peer instanceof TLRPC.TL_notifyPeer) { + TLRPC.TL_updateNotifySettings updateNotifySettings = (TLRPC.TL_updateNotifySettings) update; + if (update.notify_settings instanceof TLRPC.TL_peerNotifySettings && updateNotifySettings.peer instanceof TLRPC.TL_notifyPeer) { if (editor == null) { SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); editor = preferences.edit(); } - long dialog_id = update.peer.peer.user_id; + long dialog_id = updateNotifySettings.peer.peer.user_id; if (dialog_id == 0) { - dialog_id = -update.peer.peer.chat_id; + dialog_id = -updateNotifySettings.peer.peer.chat_id; } TLRPC.TL_dialog dialog = dialogs_dict.get(dialog_id); if (dialog != null) { @@ -3465,7 +3668,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter dialog.notify_settings.mute_until = until; } } - MessagesStorage.getInstance().setDialogFlags(dialog_id, ((long)until << 32) | 1); + MessagesStorage.getInstance().setDialogFlags(dialog_id, ((long) until << 32) | 1); } else { if (dialog != null) { dialog.notify_settings.mute_until = 0; @@ -3563,7 +3766,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter } if (!markAsReadEncrypted.isEmpty()) { for (HashMap.Entry entry : markAsReadEncrypted.entrySet()) { - NotificationCenter.getInstance().postNotificationName(NotificationCenter.messagesReadedEncrypted, entry.getKey(), entry.getValue()); + NotificationCenter.getInstance().postNotificationName(NotificationCenter.messagesReadEncrypted, entry.getKey(), entry.getValue()); long dialog_id = (long) (entry.getKey()) << 32; TLRPC.TL_dialog dialog = dialogs_dict.get(dialog_id); if (dialog != null) { @@ -3575,6 +3778,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter } } } + if (!markAsReadMessages.isEmpty()) { + NotificationCenter.getInstance().postNotificationName(NotificationCenter.messagesReadContent, markAsReadMessages); + } if (!deletedMessages.isEmpty()) { NotificationCenter.getInstance().postNotificationName(NotificationCenter.messagesDeleted, deletedMessages); for (Integer id : deletedMessages) { @@ -3601,6 +3807,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter } MessagesStorage.getInstance().markMessagesAsRead(markAsReadMessagesInbox, markAsReadMessagesOutbox, markAsReadEncrypted, true); } + if (!markAsReadMessages.isEmpty()) { + MessagesStorage.getInstance().markMessagesContentAsRead(markAsReadMessages); + } if (!deletedMessages.isEmpty()) { MessagesStorage.getInstance().markMessagesAsDeleted(deletedMessages, true); } @@ -3676,19 +3885,25 @@ public class MessagesController implements NotificationCenter.NotificationCenter } protected void updateInterfaceWithMessages(final long uid, final ArrayList messages, boolean isBroadcast) { + if (messages == null || messages.isEmpty()) { + return; + } + + boolean isEncryptedChat = ((int) uid) == 0; MessageObject lastMessage = null; - TLRPC.TL_dialog dialog = dialogs_dict.get(uid); - - boolean isEncryptedChat = ((int)uid) == 0; - - NotificationCenter.getInstance().postNotificationName(NotificationCenter.didReceivedNewMessages, uid, messages); - for (MessageObject message : messages) { - if (lastMessage == null || (!isEncryptedChat && message.getId() > lastMessage.getId() || isEncryptedChat && message.getId() < lastMessage.getId()) || message.messageOwner.date > lastMessage.messageOwner.date) { + if (lastMessage == null || (!isEncryptedChat && message.getId() > lastMessage.getId() || (isEncryptedChat || message.getId() < 0 && lastMessage.getId() < 0) && message.getId() < lastMessage.getId()) || message.messageOwner.date > lastMessage.messageOwner.date) { lastMessage = message; } } + TLRPC.TL_dialog dialog = dialogs_dict.get(uid); + NotificationCenter.getInstance().postNotificationName(NotificationCenter.didReceivedNewMessages, uid, messages); + + if (lastMessage == null) { + return; + } + boolean changed = false; if (dialog == null) { @@ -3746,13 +3961,88 @@ public class MessagesController implements NotificationCenter.NotificationCenter } }); for (TLRPC.TL_dialog d : dialogs) { - int high_id = (int)(d.id >> 32); - if ((int)d.id != 0 && high_id != 1) { + int high_id = (int) (d.id >> 32); + if ((int) d.id != 0 && high_id != 1) { dialogsServerOnly.add(d); } } } } + public static void openByUserName(String username, final BaseFragment fragment, final int type) { + if (username == null || fragment == null) { + return; + } + TLRPC.User user = MessagesController.getInstance().getUser(username); + if (user != null) { + Bundle args = new Bundle(); + args.putInt("user_id", user.id); + if (type == 0) { + fragment.presentFragment(new ProfileActivity(args)); + } else { + fragment.presentFragment(new ChatActivity(args)); + } + } else { + if (fragment.getParentActivity() == null) { + return; + } + final ProgressDialog progressDialog = new ProgressDialog(fragment.getParentActivity()); + progressDialog.setMessage(LocaleController.getString("Loading", R.string.Loading)); + progressDialog.setCanceledOnTouchOutside(false); + progressDialog.setCancelable(false); + TLRPC.TL_contacts_resolveUsername req = new TLRPC.TL_contacts_resolveUsername(); + req.username = username; + final long 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() { + try { + progressDialog.dismiss(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + if (fragment != null) { + fragment.setVisibleDialog(null); + } + if (error == null) { + TLRPC.User user = (TLRPC.User) response; + MessagesController.getInstance().putUser(user, false); + ArrayList users = new ArrayList<>(); + users.add(user); + MessagesStorage.getInstance().putUsersAndChats(users, null, false, true); + Bundle args = new Bundle(); + args.putInt("user_id", user.id); + if (fragment != null) { + if (type == 0) { + fragment.presentFragment(new ProfileActivity(args)); + } else if (type == 1) { + fragment.presentFragment(new ChatActivity(args)); + } + } + } + } + }); + } + }); + progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, LocaleController.getString("Cancel", R.string.Cancel), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + ConnectionsManager.getInstance().cancelRpc(reqId, true); + try { + dialog.dismiss(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + if (fragment != null) { + fragment.setVisibleDialog(null); + } + } + }); + fragment.setVisibleDialog(progressDialog); + progressDialog.show(); + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/android/MessagesStorage.java b/TMessagesProj/src/main/java/org/telegram/android/MessagesStorage.java index d2b7981df..7cde65697 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/MessagesStorage.java +++ b/TMessagesProj/src/main/java/org/telegram/android/MessagesStorage.java @@ -106,7 +106,7 @@ public class MessagesStorage { 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, replydata BLOB)").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, 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 dialogs(did INTEGER PRIMARY KEY, date INTEGER, unread_count INTEGER, last_mid INTEGER, inbox_max INTEGER, outbox_max 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(); database.executeFast("CREATE TABLE pending_read(uid INTEGER PRIMARY KEY, max_id INTEGER)").stepThis().dispose(); @@ -164,7 +164,7 @@ public class MessagesStorage { database.executeFast("CREATE TABLE keyvalue(id TEXT PRIMARY KEY, value TEXT)").stepThis().dispose(); //version - database.executeFast("PRAGMA user_version = 16").stepThis().dispose(); + database.executeFast("PRAGMA user_version = 17").stepThis().dispose(); } else { try { SQLiteCursor cursor = database.queryFinalized("SELECT seq, pts, date, qts, lsv, sg, pbytes FROM params WHERE id = 1"); @@ -195,7 +195,7 @@ public class MessagesStorage { } } int version = database.executeInt("PRAGMA user_version"); - if (version < 16) { + if (version < 17) { updateDbToLastVersion(version); } } @@ -282,7 +282,7 @@ public class MessagesStorage { database.executeFast("PRAGMA user_version = 4").stepThis().dispose(); version = 4; } - if (version == 4 && version < 6) { + if (version == 4) { database.executeFast("CREATE TABLE IF NOT EXISTS enc_tasks_v2(mid INTEGER PRIMARY KEY, date INTEGER)").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS date_idx_enc_tasks_v2 ON enc_tasks_v2(date);").stepThis().dispose(); database.beginTransaction(); @@ -290,12 +290,12 @@ public class MessagesStorage { SQLitePreparedStatement state = database.executeFast("REPLACE INTO enc_tasks_v2 VALUES(?, ?)"); if (cursor.next()) { int date = cursor.intValue(0); - int length = 0; + int length; ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(1)); if ((length = cursor.byteBufferValue(1, data.buffer)) != 0) { for (int a = 0; a < length / 4; a++) { state.requery(); - state.bindInteger(1, data.readInt32()); + state.bindInteger(1, data.readInt32(false)); state.bindInteger(2, date); state.step(); } @@ -313,7 +313,7 @@ public class MessagesStorage { database.executeFast("PRAGMA user_version = 6").stepThis().dispose(); version = 6; } - if (version == 6 && version < 7) { + if (version == 6) { database.executeFast("CREATE TABLE IF NOT EXISTS messages_seq(mid INTEGER PRIMARY KEY, seq_in INTEGER, seq_out INTEGER);").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS seq_idx_messages_seq ON messages_seq(seq_in, seq_out);").stepThis().dispose(); database.executeFast("ALTER TABLE enc_chats ADD COLUMN layer INTEGER default 0").stepThis().dispose(); @@ -333,7 +333,7 @@ public class MessagesStorage { database.executeFast("PRAGMA user_version = 9").stepThis().dispose(); version = 9; }*/ - if ((version == 7 || version == 8 || version == 9) && version < 10) { + if (version == 7 || version == 8 || version == 9) { 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(); @@ -343,17 +343,17 @@ public class MessagesStorage { database.executeFast("PRAGMA user_version = 10").stepThis().dispose(); version = 10; } - if (version == 10 && version < 11) { + if (version == 10) { database.executeFast("CREATE TABLE IF NOT EXISTS web_recent_v3(id TEXT, type INTEGER, image_url TEXT, thumb_url TEXT, local_url TEXT, width INTEGER, height INTEGER, size INTEGER, date INTEGER, PRIMARY KEY (id, type));").stepThis().dispose(); database.executeFast("PRAGMA user_version = 11").stepThis().dispose(); version = 11; } - if (version == 11 && version < 12) { + if (version == 11) { database.executeFast("CREATE TABLE IF NOT EXISTS stickers(id INTEGER PRIMARY KEY, data BLOB, date INTEGER);").stepThis().dispose(); database.executeFast("PRAGMA user_version = 12").stepThis().dispose(); version = 12; } - if (version == 12 && version < 13) { + if (version == 12) { database.executeFast("DROP INDEX IF EXISTS uid_mid_idx_media;").stepThis().dispose(); database.executeFast("DROP INDEX IF EXISTS mid_idx_media;").stepThis().dispose(); database.executeFast("DROP INDEX IF EXISTS uid_date_mid_idx_media;").stepThis().dispose(); @@ -370,21 +370,27 @@ public class MessagesStorage { database.executeFast("PRAGMA user_version = 13").stepThis().dispose(); version = 13; } - if (version == 13 && version < 14) { + if (version == 13) { database.executeFast("ALTER TABLE messages ADD COLUMN replydata BLOB default NULL").stepThis().dispose(); database.executeFast("PRAGMA user_version = 14").stepThis().dispose(); version = 14; } - if (version == 14 && version < 15) { + if (version == 14) { database.executeFast("CREATE TABLE IF NOT EXISTS hashtag_recent_v2(id TEXT PRIMARY KEY, date INTEGER);").stepThis().dispose(); database.executeFast("PRAGMA user_version = 15").stepThis().dispose(); version = 15; } - if (version == 15 && version < 16) { + if (version == 15) { database.executeFast("CREATE TABLE IF NOT EXISTS webpage_pending(id INTEGER, mid INTEGER, PRIMARY KEY (id, mid));").stepThis().dispose(); database.executeFast("PRAGMA user_version = 16").stepThis().dispose(); version = 16; } + if (version == 16) { + database.executeFast("ALTER TABLE dialogs ADD COLUMN inbox_max INTEGER default 0").stepThis().dispose(); + database.executeFast("ALTER TABLE dialogs ADD COLUMN outbox_max INTEGER default 0").stepThis().dispose(); + database.executeFast("PRAGMA user_version = 17").stepThis().dispose(); + //version = 17; + } } catch (Exception e) { FileLog.e("tmessages", e); } @@ -523,12 +529,12 @@ public class MessagesStorage { ArrayList chatIds = new ArrayList<>(); ArrayList encryptedChatIds = new ArrayList<>(); - cursor = database.queryFinalized("SELECT read_state, data, send_state, mid, date, uid FROM messages WHERE uid IN (" + ids.toString() + ") AND out = 0 AND read_state = 0 ORDER BY date DESC LIMIT 50"); + cursor = database.queryFinalized("SELECT read_state, data, send_state, mid, date, uid FROM messages WHERE uid IN (" + ids.toString() + ") AND out = 0 AND read_state IN(0,2) ORDER BY date DESC LIMIT 50"); while (cursor.next()) { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(1)); if (data != null && cursor.byteBufferValue(1, data.buffer) != 0) { - TLRPC.Message message = (TLRPC.Message)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); - MessageObject.setIsUnread(message, cursor.intValue(0) != 1); + TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); + MessageObject.setUnreadFlags(message, cursor.intValue(0)); message.id = cursor.intValue(3); message.date = cursor.intValue(4); message.dialog_id = cursor.longValue(5); @@ -758,17 +764,22 @@ public class MessagesStorage { public void run() { try { SQLiteCursor cursor = database.queryFinalized("SELECT data FROM wallpapers WHERE 1"); - ArrayList wallPapers = new ArrayList<>(); + final ArrayList wallPapers = new ArrayList<>(); while (cursor.next()) { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - TLRPC.WallPaper wallPaper = (TLRPC.WallPaper) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.WallPaper wallPaper = TLRPC.WallPaper.TLdeserialize(data, data.readInt32(false), false); wallPapers.add(wallPaper); } buffersStorage.reuseFreeBuffer(data); } cursor.dispose(); - NotificationCenter.getInstance().postNotificationName(NotificationCenter.wallpapersDidLoaded, wallPapers); + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + NotificationCenter.getInstance().postNotificationName(NotificationCenter.wallpapersDidLoaded, wallPapers); + } + }); } catch (Exception e) { FileLog.e("tmessages", e); } @@ -876,7 +887,7 @@ public class MessagesStorage { while (cursor.next()) { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - TLRPC.Message message = (TLRPC.Message) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); if (message == null || message.media == null) { continue; } @@ -950,7 +961,7 @@ public class MessagesStorage { while (cursor.next()) { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - TLRPC.Photo photo = (TLRPC.Photo)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.Photo photo = TLRPC.Photo.TLdeserialize(data, data.readInt32(false), false); res.photos.add(photo); } buffersStorage.reuseFreeBuffer(data); @@ -1063,9 +1074,9 @@ public class MessagesStorage { int minDate = Integer.MAX_VALUE; SparseArray> messages = new SparseArray<>(); StringBuilder mids = new StringBuilder(); - SQLiteCursor cursor = null; + SQLiteCursor cursor; if (random_ids == null) { - cursor = database.queryFinalized(String.format(Locale.US, "SELECT mid, ttl FROM messages WHERE uid = %d AND out = %d AND read_state = 1 AND ttl > 0 AND date <= %d AND send_state = 0 AND media != 1", ((long) chat_id) << 32, isOut, time)); + cursor = database.queryFinalized(String.format(Locale.US, "SELECT mid, ttl FROM messages WHERE uid = %d AND out = %d AND read_state != 0 AND ttl > 0 AND date <= %d AND send_state = 0 AND media != 1", ((long) chat_id) << 32, isOut, time)); } else { String ids = TextUtils.join(",", random_ids); cursor = database.queryFinalized(String.format(Locale.US, "SELECT m.mid, m.ttl FROM messages as m INNER JOIN randoms as r ON m.mid = r.mid WHERE r.random_id IN (%s)", ids)); @@ -1147,7 +1158,7 @@ public class MessagesStorage { cursor.dispose(); } else if (inbox != null && !inbox.isEmpty()) { for (HashMap.Entry entry : inbox.entrySet()) { - SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT COUNT(mid) FROM messages WHERE uid = %d AND mid <= %d AND read_state = 0 AND out = 0", entry.getKey(), entry.getValue())); + SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT COUNT(mid) FROM messages WHERE uid = %d AND mid <= %d AND read_state IN(0,2) AND out = 0", entry.getKey(), entry.getValue())); if (cursor.next()) { int count = cursor.intValue(0); if (count == 0) { @@ -1255,7 +1266,7 @@ public class MessagesStorage { if (cursor.next()) { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - info = (TLRPC.ChatParticipants)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + info = TLRPC.ChatParticipants.TLdeserialize(data, data.readInt32(false), false); } buffersStorage.reuseFreeBuffer(data); } @@ -1318,7 +1329,7 @@ public class MessagesStorage { if (cursor.next()) { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - info = (TLRPC.ChatParticipants)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + info = TLRPC.ChatParticipants.TLdeserialize(data, data.readInt32(false), false); } buffersStorage.reuseFreeBuffer(data); } @@ -1383,14 +1394,14 @@ public class MessagesStorage { int lower_id = (int)dialog_id; if (lower_id != 0) { - state = database.executeFast("UPDATE messages SET read_state = 1 WHERE uid = ? AND mid <= ? AND read_state = 0 AND out = 0"); + state = database.executeFast("UPDATE messages SET read_state = read_state | 1 WHERE uid = ? AND mid <= ? AND read_state IN(0,2) AND out = 0"); state.requery(); state.bindLong(1, dialog_id); state.bindInteger(2, max_id); state.step(); state.dispose(); } else { - state = database.executeFast("UPDATE messages SET read_state = 1 WHERE uid = ? AND date <= ? AND read_state = 0 AND out = 0"); + state = database.executeFast("UPDATE messages SET read_state = read_state | 1 WHERE uid = ? AND date <= ? AND read_state IN(0,2) AND out = 0"); state.requery(); state.bindLong(1, dialog_id); state.bindInteger(2, max_date); @@ -1613,9 +1624,9 @@ public class MessagesStorage { while (cursor.next()) { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(1)); if (data != null && cursor.byteBufferValue(1, data.buffer) != 0) { - TLRPC.Message message = (TLRPC.Message)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); if (!messageHashMap.containsKey(message.id)) { - MessageObject.setIsUnread(message, cursor.intValue(0) != 1); + MessageObject.setUnreadFlags(message, cursor.intValue(0)); message.id = cursor.intValue(3); message.date = cursor.intValue(4); if (!cursor.isNull(5)) { @@ -1757,7 +1768,7 @@ public class MessagesStorage { ArrayList replyMessages = new ArrayList<>(); HashMap> replyMessageOwners = new HashMap<>(); - SQLiteCursor cursor = null; + SQLiteCursor cursor; int lower_id = (int)dialog_id; if (lower_id != 0) { @@ -1798,14 +1809,14 @@ public class MessagesStorage { } 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)); + cursor = database.queryFinalized(String.format(Locale.US, "SELECT min(mid), max(date) FROM messages WHERE uid = %d AND out = 0 AND read_state IN(0,2) AND mid > 0", dialog_id)); if (cursor.next()) { min_unread_id = cursor.intValue(0); max_unread_date = cursor.intValue(1); } cursor.dispose(); if (min_unread_id != 0) { - cursor = database.queryFinalized(String.format(Locale.US, "SELECT COUNT(*) FROM messages WHERE uid = %d AND mid >= %d AND out = 0 AND read_state = 0", dialog_id, min_unread_id)); + cursor = database.queryFinalized(String.format(Locale.US, "SELECT COUNT(*) FROM messages WHERE uid = %d AND mid >= %d AND out = 0 AND read_state IN(0,2)", dialog_id, min_unread_id)); if (cursor.next()) { count_unread = cursor.intValue(0); } @@ -1843,14 +1854,14 @@ public class MessagesStorage { } 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)); + cursor = database.queryFinalized(String.format(Locale.US, "SELECT max(mid), max(date) FROM messages WHERE uid = %d AND out = 0 AND read_state IN(0,2) AND mid < 0", dialog_id)); if (cursor.next()) { min_unread_id = cursor.intValue(0); max_unread_date = cursor.intValue(1); } cursor.dispose(); if (min_unread_id != 0) { - cursor = database.queryFinalized(String.format(Locale.US, "SELECT COUNT(*) FROM messages WHERE uid = %d AND mid <= %d AND out = 0 AND read_state = 0", dialog_id, min_unread_id)); + cursor = database.queryFinalized(String.format(Locale.US, "SELECT COUNT(*) FROM messages WHERE uid = %d AND mid <= %d AND out = 0 AND read_state IN(0,2)", dialog_id, min_unread_id)); if (cursor.next()) { count_unread = cursor.intValue(0); } @@ -1876,8 +1887,8 @@ public class MessagesStorage { while (cursor.next()) { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(1)); if (data != null && cursor.byteBufferValue(1, data.buffer) != 0) { - TLRPC.Message message = (TLRPC.Message) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); - MessageObject.setIsUnread(message, cursor.intValue(0) != 1); + TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); + MessageObject.setUnreadFlags(message, cursor.intValue(0)); message.id = cursor.intValue(3); message.date = cursor.intValue(4); message.dialog_id = dialog_id; @@ -1900,7 +1911,7 @@ public class MessagesStorage { if (!cursor.isNull(6)) { ByteBufferDesc data2 = buffersStorage.getFreeBuffer(cursor.byteArrayLength(6)); if (data2 != null && cursor.byteBufferValue(6, data2.buffer) != 0) { - message.replyMessage = (TLRPC.Message) TLClassStore.Instance().TLdeserialize(data2, data2.readInt32()); + message.replyMessage = TLRPC.Message.TLdeserialize(data2, data2.readInt32(false), false); if (message.replyMessage != null) { fromUser.add(message.replyMessage.from_id); if (message.replyMessage.action != null && message.replyMessage.action.user_id != 0) { @@ -1987,7 +1998,7 @@ public class MessagesStorage { while (cursor.next()) { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - TLRPC.Message message = (TLRPC.Message) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); message.id = cursor.intValue(1); message.date = cursor.intValue(2); message.dialog_id = dialog_id; @@ -2091,7 +2102,7 @@ public class MessagesStorage { if (cursor.next()) { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - TLObject file = TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLObject file = TLClassStore.Instance().TLdeserialize(data, data.readInt32(false), false); if (file != null) { result.add(file); } @@ -2374,11 +2385,13 @@ public class MessagesStorage { buffersStorage.reuseFreeBuffer(data5); if (dialog != null) { - state = database.executeFast("REPLACE INTO dialogs(did, date, unread_count, last_mid) VALUES(?, ?, ?, ?)"); + state = database.executeFast("REPLACE INTO dialogs(did, date, unread_count, last_mid, inbox_max, outbox_max) VALUES(?, ?, ?, ?, ?, ?)"); state.bindLong(1, dialog.id); state.bindInteger(2, dialog.last_message_date); state.bindInteger(3, dialog.unread_count); state.bindInteger(4, dialog.top_message); + state.bindInteger(5, dialog.read_inbox_max_id); + state.bindInteger(6, 0); state.step(); state.dispose(); } @@ -2469,7 +2482,7 @@ public class MessagesStorage { try { 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()); + TLRPC.User user = TLRPC.User.TLdeserialize(data, data.readInt32(false), false); if (user != null) { if (user.status != null) { user.status.expires = cursor.intValue(1); @@ -2494,7 +2507,7 @@ public class MessagesStorage { try { 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()); + TLRPC.Chat chat = TLRPC.Chat.TLdeserialize(data, data.readInt32(false), false); if (chat != null) { result.add(chat); } @@ -2511,13 +2524,12 @@ public class MessagesStorage { if (chatsToLoad == null || chatsToLoad.length() == 0 || result == null) { return; } - //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)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - TLRPC.EncryptedChat chat = (TLRPC.EncryptedChat)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.EncryptedChat chat = TLRPC.EncryptedChat.TLdeserialize(data, data.readInt32(false), false); if (chat != null) { chat.user_id = cursor.intValue(1); if (usersToLoad != null && !usersToLoad.contains(chat.user_id)) { @@ -2634,7 +2646,7 @@ public class MessagesStorage { downloadObject.id = cursor.longValue(0); ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(2)); if (data != null && cursor.byteBufferValue(2, data.buffer) != 0) { - downloadObject.object = TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + downloadObject.object = TLClassStore.Instance().TLdeserialize(data, data.readInt32(false), false); } buffersStorage.reuseFreeBuffer(data); objects.add(downloadObject); @@ -2691,7 +2703,7 @@ public class MessagesStorage { int mid = cursor.intValue(0); ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(1)); if (data != null && cursor.byteBufferValue(1, data.buffer) != 0) { - TLRPC.Message message = (TLRPC.Message)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); if (message.media instanceof TLRPC.TL_messageMediaWebPage) { message.id = mid; message.media.webpage = webPages.get(message.media.webpage.id); @@ -2865,7 +2877,7 @@ public class MessagesStorage { state.bindInteger(1, messageId); state.bindLong(2, dialog_id); - state.bindInteger(3, (MessageObject.isUnread(message) ? 0 : 1)); + state.bindInteger(3, MessageObject.getUnreadFlags(message)); state.bindInteger(4, message.send_state); state.bindInteger(5, message.date); state.bindByteBuffer(6, data.buffer); @@ -2954,7 +2966,7 @@ public class MessagesStorage { state4.dispose(); state5.dispose(); - state = database.executeFast("REPLACE INTO dialogs(did, date, unread_count, last_mid) VALUES(?, ?, ?, ?)"); + state = database.executeFast("REPLACE INTO dialogs(did, date, unread_count, last_mid, inbox_max, outbox_max) VALUES(?, ?, ?, ?, ?, ?)"); for (HashMap.Entry pair : messagesMap.entrySet()) { Long key = pair.getKey(); @@ -2987,6 +2999,8 @@ public class MessagesStorage { } state.bindInteger(3, old_unread_count + unread_count); state.bindInteger(4, messageId); + state.bindInteger(5, 0); + state.bindInteger(6, 0); state.step(); } state.dispose(); @@ -3232,7 +3246,6 @@ public class MessagesStorage { } finally { if (state != null) { state.dispose(); - state = null; } } @@ -3294,8 +3307,10 @@ public class MessagesStorage { TLRPC.User updateUser = usersDict.get(user.id); if (updateUser != null) { if (updateUser.first_name != null && updateUser.last_name != null) { - user.first_name = updateUser.first_name; - user.last_name = updateUser.last_name; + if (!(user instanceof TLRPC.TL_userContact)) { + user.first_name = updateUser.first_name; + user.last_name = updateUser.last_name; + } user.username = updateUser.username; } else if (updateUser.photo != null) { user.photo = updateUser.photo; @@ -3337,25 +3352,22 @@ public class MessagesStorage { } private void markMessagesAsReadInternal(HashMap inbox, HashMap outbox, HashMap encryptedMessages) { - if (Thread.currentThread().getId() != storageQueue.getId()) { - throw new RuntimeException("wrong db thread"); - } try { if (inbox != null) { for (HashMap.Entry entry : inbox.entrySet()) { - database.executeFast(String.format(Locale.US, "UPDATE messages SET read_state = 1 WHERE uid = %d AND mid <= %d AND read_state = 0 AND out = 0", entry.getKey(), entry.getValue())).stepThis().dispose(); + database.executeFast(String.format(Locale.US, "UPDATE messages SET read_state = read_state | 1 WHERE uid = %d AND mid > 0 AND mid <= %d AND read_state IN(0,2) AND out = 0", entry.getKey(), entry.getValue())).stepThis().dispose(); } } if (outbox != null) { for (HashMap.Entry entry : outbox.entrySet()) { - database.executeFast(String.format(Locale.US, "UPDATE messages SET read_state = 1 WHERE uid = %d AND mid <= %d AND read_state = 0 AND out = 1", entry.getKey(), entry.getValue())).stepThis().dispose(); + database.executeFast(String.format(Locale.US, "UPDATE messages SET read_state = read_state | 1 WHERE uid = %d AND mid > 0 AND mid <= %d AND read_state IN(0,2) AND out = 1", entry.getKey(), entry.getValue())).stepThis().dispose(); } } if (encryptedMessages != null && !encryptedMessages.isEmpty()) { for (HashMap.Entry entry : encryptedMessages.entrySet()) { long dialog_id = ((long)entry.getKey()) << 32; int max_date = entry.getValue(); - SQLitePreparedStatement state = database.executeFast("UPDATE messages SET read_state = 1 WHERE uid = ? AND date <= ? AND read_state = 0 AND out = 1"); + SQLitePreparedStatement state = database.executeFast("UPDATE messages SET read_state = read_state | 1 WHERE uid = ? AND date <= ? AND read_state IN(0,2) AND out = 1"); state.requery(); state.bindLong(1, dialog_id); state.bindInteger(2, max_date); @@ -3368,6 +3380,22 @@ public class MessagesStorage { } } + public void markMessagesContentAsRead(final ArrayList mids) { + if (mids == null || mids.isEmpty()) { + return; + } + storageQueue.postRunnable(new Runnable() { + @Override + public void run() { + try { + database.executeFast(String.format(Locale.US, "UPDATE messages SET read_state = read_state | 2 WHERE mid IN (%s)", TextUtils.join(",", mids))).stepThis().dispose(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + }); + } + public void markMessagesAsRead(final HashMap inbox, final HashMap outbox, final HashMap encryptedMessages, boolean useQueue) { if (useQueue) { storageQueue.postRunnable(new Runnable() { @@ -3433,7 +3461,7 @@ public class MessagesStorage { } ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(1)); if (data != null && cursor.byteBufferValue(1, data.buffer) != 0) { - TLRPC.Message message = (TLRPC.Message)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); if (message == null || message.media == null) { continue; } @@ -3527,8 +3555,8 @@ public class MessagesStorage { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(4)); if (data != null && cursor.byteBufferValue(4, data.buffer) != 0) { - TLRPC.Message message = (TLRPC.Message)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); - MessageObject.setIsUnread(message, cursor.intValue(5) != 1); + TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); + MessageObject.setUnreadFlags(message, cursor.intValue(5)); message.id = cursor.intValue(6); message.send_state = cursor.intValue(7); int date = cursor.intValue(8); @@ -3667,7 +3695,7 @@ public class MessagesStorage { message.serializeToStream(data); state.bindInteger(1, message.id); state.bindLong(2, dialog_id); - state.bindInteger(3, (MessageObject.isUnread(message) ? 0 : 1)); + state.bindInteger(3, MessageObject.getUnreadFlags(message)); state.bindInteger(4, message.send_state); state.bindInteger(5, message.date); state.bindByteBuffer(6, data.buffer); @@ -3732,9 +3760,9 @@ public class MessagesStorage { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(4)); if (data != null && cursor.byteBufferValue(4, data.buffer) != 0) { - TLRPC.Message message = (TLRPC.Message)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); if (message != null) { - MessageObject.setIsUnread(message, cursor.intValue(5) != 1); + MessageObject.setUnreadFlags(message, cursor.intValue(5)); message.id = cursor.intValue(6); int date = cursor.intValue(9); if (date != 0) { @@ -3831,7 +3859,7 @@ public class MessagesStorage { if (!dialogs.dialogs.isEmpty()) { SQLitePreparedStatement state = database.executeFast("REPLACE INTO messages VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)"); - SQLitePreparedStatement state2 = database.executeFast("REPLACE INTO dialogs(did, date, unread_count, last_mid) VALUES(?, ?, ?, ?)"); + SQLitePreparedStatement state2 = database.executeFast("REPLACE INTO dialogs(did, date, unread_count, last_mid, inbox_max, outbox_max) VALUES(?, ?, ?, ?, ?, ?)"); SQLitePreparedStatement state3 = database.executeFast("REPLACE INTO media_v2 VALUES(?, ?, ?, ?, ?)"); SQLitePreparedStatement state4 = database.executeFast("REPLACE INTO dialog_settings VALUES(?, ?)"); @@ -3850,7 +3878,7 @@ public class MessagesStorage { state.bindInteger(1, message.id); state.bindInteger(2, uid); - state.bindInteger(3, (MessageObject.isUnread(message) ? 0 : 1)); + state.bindInteger(3, MessageObject.getUnreadFlags(message)); state.bindInteger(4, message.send_state); state.bindInteger(5, message.date); state.bindByteBuffer(6, data.buffer); @@ -3863,6 +3891,8 @@ public class MessagesStorage { state2.bindInteger(2, message.date); state2.bindInteger(3, dialog.unread_count); state2.bindInteger(4, dialog.top_message); + state2.bindInteger(5, dialog.read_inbox_max_id); + state2.bindInteger(6, 0); state2.step(); state4.bindLong(1, uid); diff --git a/TMessagesProj/src/main/java/org/telegram/android/NativeLoader.java b/TMessagesProj/src/main/java/org/telegram/android/NativeLoader.java index 6eab0a11b..7ea11323e 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/NativeLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/android/NativeLoader.java @@ -23,7 +23,7 @@ import java.util.zip.ZipFile; public class NativeLoader { - private final static int LIB_VERSION = 7; + private final static int LIB_VERSION = 8; 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"; diff --git a/TMessagesProj/src/main/java/org/telegram/android/NotificationCenter.java b/TMessagesProj/src/main/java/org/telegram/android/NotificationCenter.java index 962de6977..2092d378c 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/NotificationCenter.java +++ b/TMessagesProj/src/main/java/org/telegram/android/NotificationCenter.java @@ -8,6 +8,10 @@ package org.telegram.android; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.BuildVars; +import org.telegram.messenger.FileLog; + import java.util.ArrayList; import java.util.HashMap; @@ -32,7 +36,7 @@ public class NotificationCenter { public static final int mediaDidLoaded = totalEvents++; public static final int mediaCountDidLoaded = totalEvents++; public static final int encryptedChatUpdated = totalEvents++; - public static final int messagesReadedEncrypted = totalEvents++; + public static final int messagesReadEncrypted = totalEvents++; public static final int encryptedChatCreated = totalEvents++; public static final int userPhotosLoaded = totalEvents++; public static final int removeAllMessagesFromDialog = totalEvents++; @@ -51,11 +55,13 @@ public class NotificationCenter { public static final int didSetPasscode = totalEvents++; public static final int didSetTwoStepPassword = totalEvents++; public static final int screenStateChanged = totalEvents++; - public static final int appSwitchedToForeground = totalEvents++; public static final int didLoadedReplyMessages = totalEvents++; public static final int newSessionReceived = totalEvents++; public static final int didReceivedWebpages = totalEvents++; public static final int didReceivedWebpagesInUpdates = totalEvents++; + public static final int stickersDidLoaded = totalEvents++; + public static final int didReplacedPhotoInMemCache = totalEvents++; + public static final int messagesReadContent = totalEvents++; public static final int httpFileDidLoaded = totalEvents++; public static final int httpFileDidFailedLoad = totalEvents++; @@ -91,14 +97,31 @@ public class NotificationCenter { public static final int audioDidStarted = totalEvents++; public static final int audioRouteChanged = totalEvents++; - final private HashMap> observers = new HashMap<>(); - - final private HashMap removeAfterBroadcast = new HashMap<>(); - final private HashMap addAfterBroadcast = new HashMap<>(); + private HashMap> observers = new HashMap<>(); + private HashMap removeAfterBroadcast = new HashMap<>(); + private HashMap addAfterBroadcast = new HashMap<>(); + private ArrayList delayedPosts = new ArrayList<>(10); private int broadcasting = 0; + private boolean animationInProgress; + + public interface NotificationCenterDelegate { + void didReceivedNotification(int id, Object... args); + } + + private class DelayedPost { + + private DelayedPost(int id, Object[] args) { + this.id = id; + this.args = args; + } + + private int id; + private Object[] args; + } private static volatile NotificationCenter Instance = null; + public static NotificationCenter getInstance() { NotificationCenter localInstance = Instance; if (localInstance == null) { @@ -112,66 +135,97 @@ public class NotificationCenter { return localInstance; } - public interface NotificationCenterDelegate { - void didReceivedNotification(int id, Object... args); + public void setAnimationInProgress(boolean value) { + animationInProgress = value; + if (!animationInProgress && !delayedPosts.isEmpty()) { + for (DelayedPost delayedPost : delayedPosts) { + postNotificationNameInternal(delayedPost.id, true, delayedPost.args); + } + delayedPosts.clear(); + } } public void postNotificationName(int id, Object... args) { - synchronized (observers) { - broadcasting++; - ArrayList objects = observers.get(id); - if (objects != null) { - for (Object obj : objects) { - ((NotificationCenterDelegate)obj).didReceivedNotification(id, args); - } + boolean allowDuringAnimation = false; + if (id == dialogsNeedReload || id == closeChats || id == messagesDidLoaded || id == mediaCountDidLoaded || id == mediaDidLoaded) { + allowDuringAnimation = true; + } + postNotificationNameInternal(id, allowDuringAnimation, args); + } + + public void postNotificationNameInternal(int id, boolean allowDuringAnimation, Object... args) { + if (BuildVars.DEBUG_VERSION) { + if (Thread.currentThread() != ApplicationLoader.applicationHandler.getLooper().getThread()) { + throw new RuntimeException("postNotificationName allowed only from MAIN thread"); } - broadcasting--; - if (broadcasting == 0) { - if (!removeAfterBroadcast.isEmpty()) { - for (HashMap.Entry entry : removeAfterBroadcast.entrySet()) { - removeObserver(entry.getValue(), entry.getKey()); - } - removeAfterBroadcast.clear(); + } + if (!allowDuringAnimation && animationInProgress) { + DelayedPost delayedPost = new DelayedPost(id, args); + delayedPosts.add(delayedPost); + if (BuildVars.DEBUG_VERSION) { + FileLog.e("tmessages", "delay post notification " + id + " with args count = " + args.length); + } + return; + } + broadcasting++; + ArrayList objects = observers.get(id); + if (objects != null) { + for (Object obj : objects) { + ((NotificationCenterDelegate) obj).didReceivedNotification(id, args); + } + } + broadcasting--; + if (broadcasting == 0) { + if (!removeAfterBroadcast.isEmpty()) { + for (HashMap.Entry entry : removeAfterBroadcast.entrySet()) { + removeObserver(entry.getValue(), entry.getKey()); } - if (!addAfterBroadcast.isEmpty()) { - for (HashMap.Entry entry : addAfterBroadcast.entrySet()) { - addObserver(entry.getValue(), entry.getKey()); - } - addAfterBroadcast.clear(); + removeAfterBroadcast.clear(); + } + if (!addAfterBroadcast.isEmpty()) { + for (HashMap.Entry entry : addAfterBroadcast.entrySet()) { + addObserver(entry.getValue(), entry.getKey()); } + addAfterBroadcast.clear(); } } } public void addObserver(Object observer, int id) { - synchronized (observers) { - if (broadcasting != 0) { - addAfterBroadcast.put(id, observer); - return; + if (BuildVars.DEBUG_VERSION) { + if (Thread.currentThread() != ApplicationLoader.applicationHandler.getLooper().getThread()) { + throw new RuntimeException("addObserver allowed only from MAIN thread"); } - ArrayList objects = observers.get(id); - if (objects == null) { - observers.put(id, (objects = new ArrayList<>())); - } - if (objects.contains(observer)) { - return; - } - objects.add(observer); } + if (broadcasting != 0) { + addAfterBroadcast.put(id, observer); + return; + } + ArrayList objects = observers.get(id); + if (objects == null) { + observers.put(id, (objects = new ArrayList<>())); + } + if (objects.contains(observer)) { + return; + } + objects.add(observer); } public void removeObserver(Object observer, int id) { - synchronized (observers) { - if (broadcasting != 0) { - removeAfterBroadcast.put(id, observer); - return; + if (BuildVars.DEBUG_VERSION) { + if (Thread.currentThread() != ApplicationLoader.applicationHandler.getLooper().getThread()) { + throw new RuntimeException("removeObserver allowed only from MAIN thread"); } - ArrayList objects = observers.get(id); - if (objects != null) { - objects.remove(observer); - if (objects.size() == 0) { - observers.remove(id); - } + } + if (broadcasting != 0) { + removeAfterBroadcast.put(id, observer); + return; + } + ArrayList objects = observers.get(id); + if (objects != null) { + objects.remove(observer); + if (objects.size() == 0) { + observers.remove(id); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/android/NotificationsController.java b/TMessagesProj/src/main/java/org/telegram/android/NotificationsController.java index 1f31a687b..67fb8fb3f 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/NotificationsController.java +++ b/TMessagesProj/src/main/java/org/telegram/android/NotificationsController.java @@ -8,6 +8,7 @@ package org.telegram.android; +import android.annotation.SuppressLint; import android.app.Activity; import android.app.AlarmManager; import android.app.PendingIntent; @@ -17,10 +18,10 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.content.res.AssetFileDescriptor; +import android.graphics.Point; import android.graphics.drawable.BitmapDrawable; import android.media.AudioManager; -import android.media.MediaPlayer; +import android.media.SoundPool; import android.net.Uri; import android.os.Build; import android.os.SystemClock; @@ -54,12 +55,16 @@ public class NotificationsController { private DispatchQueue notificationsQueue = new DispatchQueue("notificationsQueue"); private ArrayList pushMessages = new ArrayList<>(); + private ArrayList delayedPushMessages = new ArrayList<>(); private HashMap pushMessagesDict = new HashMap<>(); + private HashMap smartNotificationsDialogs = new HashMap<>(); private NotificationManagerCompat notificationManager = null; private HashMap pushDialogs = new HashMap<>(); - private HashMap wearNoticationsIds = new HashMap<>(); + private HashMap wearNotificationsIds = new HashMap<>(); + private HashMap autoNotificationsIds = new HashMap<>(); private HashMap pushDialogsOverrideMention = new HashMap<>(); private int wearNotificationId = 10000; + private int autoNotificationId = 20000; public ArrayList popupMessages = new ArrayList<>(); private long openned_dialog_id = 0; private int total_unread_count = 0; @@ -67,10 +72,14 @@ public class NotificationsController { private boolean notifyCheck = false; private int lastOnlineFromOtherDevice = 0; private boolean inChatSoundEnabled = true; + private int lastBadgeCount; + private String launcherClassName; private long lastSoundPlay; - private MediaPlayer mediaPlayerIn; - private MediaPlayer mediaPlayerOut; + private long lastSoundOutPlay; + private SoundPool soundPool; + private int soundIn; + private int soundOut; protected AudioManager audioManager; private static volatile NotificationsController Instance = null; @@ -94,7 +103,6 @@ public class NotificationsController { try { audioManager = (AudioManager) ApplicationLoader.applicationContext.getSystemService(Context.AUDIO_SERVICE); - //mediaPlayer = new MediaPlayer(); } catch (Exception e) { FileLog.e("tmessages", e); } @@ -108,8 +116,10 @@ public class NotificationsController { pushMessagesDict.clear(); pushDialogs.clear(); popupMessages.clear(); - wearNoticationsIds.clear(); + wearNotificationsIds.clear(); + autoNotificationsIds.clear(); notifyCheck = false; + lastBadgeCount = 0; SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Context.MODE_PRIVATE); SharedPreferences.Editor editor = preferences.edit(); editor.clear(); @@ -187,7 +197,7 @@ public class NotificationsController { msg = LocaleController.formatString("NotificationMessageVideo", R.string.NotificationMessageVideo, ContactsController.formatName(user.first_name, user.last_name)); } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaContact) { msg = LocaleController.formatString("NotificationMessageContact", R.string.NotificationMessageContact, ContactsController.formatName(user.first_name, user.last_name)); - } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeo) { + } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeo || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVenue) { msg = LocaleController.formatString("NotificationMessageMap", R.string.NotificationMessageMap, ContactsController.formatName(user.first_name, user.last_name)); } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument) { if (messageObject.isSticker()) { @@ -214,8 +224,14 @@ public class NotificationsController { if (u2 == null) { return null; } - msg = LocaleController.formatString("NotificationGroupAddMember", R.string.NotificationGroupAddMember, ContactsController.formatName(user.first_name, user.last_name), chat.title, ContactsController.formatName(u2.first_name, u2.last_name)); + if (user.id == u2.id) { + msg = LocaleController.formatString("NotificationGroupAddSelf", R.string.NotificationGroupAddSelf, ContactsController.formatName(user.first_name, user.last_name), chat.title); + } else { + msg = LocaleController.formatString("NotificationGroupAddMember", R.string.NotificationGroupAddMember, ContactsController.formatName(user.first_name, user.last_name), chat.title, ContactsController.formatName(u2.first_name, u2.last_name)); + } } + } else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionChatJoinedByLink) { + msg = LocaleController.formatString("NotificationInvitedToGroupByLink", R.string.NotificationInvitedToGroupByLink, ContactsController.formatName(user.first_name, user.last_name), chat.title); } else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionChatEditTitle) { msg = LocaleController.formatString("NotificationEditedGroupName", R.string.NotificationEditedGroupName, ContactsController.formatName(user.first_name, user.last_name), messageObject.messageOwner.action.title); } else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionChatEditPhoto || messageObject.messageOwner.action instanceof TLRPC.TL_messageActionChatDeletePhoto) { @@ -248,7 +264,7 @@ public class NotificationsController { msg = LocaleController.formatString("NotificationMessageGroupVideo", R.string.NotificationMessageGroupVideo, ContactsController.formatName(user.first_name, user.last_name), chat.title); } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaContact) { msg = LocaleController.formatString("NotificationMessageGroupContact", R.string.NotificationMessageGroupContact, ContactsController.formatName(user.first_name, user.last_name), chat.title); - } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeo) { + } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeo || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVenue) { msg = LocaleController.formatString("NotificationMessageGroupMap", R.string.NotificationMessageGroupMap, ContactsController.formatName(user.first_name, user.last_name), chat.title); } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument) { if (messageObject.isSticker()) { @@ -286,14 +302,14 @@ public class NotificationsController { private void scheduleNotificationDelay(boolean onlineReason) { try { - FileLog.e("tmessages", "delay notification start"); + FileLog.e("tmessages", "delay notification start, onlineReason = " + onlineReason); AlarmManager alarm = (AlarmManager) ApplicationLoader.applicationContext.getSystemService(Context.ALARM_SERVICE); PendingIntent pintent = PendingIntent.getService(ApplicationLoader.applicationContext, 0, new Intent(ApplicationLoader.applicationContext, NotificationDelay.class), 0); SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); if (onlineReason) { alarm.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 3 * 1000, pintent); } else { - alarm.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 500, pintent); + alarm.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 1000, pintent); } } catch (Exception e) { FileLog.e("tmessages", e); @@ -302,7 +318,10 @@ public class NotificationsController { protected void notificationDelayReached() { FileLog.e("tmessages", "delay reached"); - showOrUpdateNotification(true); + if (!delayedPushMessages.isEmpty()) { + showOrUpdateNotification(true); + delayedPushMessages.clear(); + } } protected void repeatNotificationMaybe() { @@ -319,6 +338,7 @@ public class NotificationsController { AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { + FileLog.e("tmessages", "set last online from other device = " + time); lastOnlineFromOtherDevice = time; } }); @@ -359,34 +379,52 @@ public class NotificationsController { int needVibrate = 0; String choosenSoundPath = null; int ledColor = 0xff00ff00; - boolean inAppSounds = false; - boolean inAppVibrate = false; + boolean inAppSounds; + boolean inAppVibrate; boolean inAppPreview = false; - boolean inAppPriority = false; + boolean inAppPriority; int priority = 0; - int priority_override = 0; - int vibrate_override = 0; + int priorityOverride; + int vibrateOverride; SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Context.MODE_PRIVATE); - int notify_override = preferences.getInt("notify2_" + override_dialog_id, 0); - if (notify_override == 3) { - int mute_until = preferences.getInt("notifyuntil_" + override_dialog_id, 0); - if (mute_until >= ConnectionsManager.getInstance().getCurrentTime()) { - notify_override = 2; - } - } - if (!notifyAboutLast || notify_override == 2 || (!preferences.getBoolean("EnableAll", true) || chat_id != 0 && !preferences.getBoolean("EnableGroup", true)) && notify_override == 0) { + int notifyOverride = getNotifyOverride(preferences, override_dialog_id); + if (!notifyAboutLast || notifyOverride == 2 || (!preferences.getBoolean("EnableAll", true) || chat_id != 0 && !preferences.getBoolean("EnableGroup", true)) && notifyOverride == 0) { notifyDisabled = true; } + if (!notifyDisabled && dialog_id == override_dialog_id && chat != null) { + int notifyMaxCount = preferences.getInt("smart_max_count_" + dialog_id, 2); + int notifyDelay = preferences.getInt("smart_delay_" + dialog_id, 3 * 60); + if (notifyMaxCount != 0) { + Point dialogInfo = smartNotificationsDialogs.get(dialog_id); + if (dialogInfo == null) { + dialogInfo = new Point(1, (int) (System.currentTimeMillis() / 1000)); + smartNotificationsDialogs.put(dialog_id, dialogInfo); + } else { + int lastTime = dialogInfo.y; + if (lastTime + notifyDelay < System.currentTimeMillis() / 1000) { + dialogInfo.set(1, (int) (System.currentTimeMillis() / 1000)); + } else { + int count = dialogInfo.x; + if (count < notifyMaxCount) { + dialogInfo.set(count + 1, (int) (System.currentTimeMillis() / 1000)); + } else { + notifyDisabled = true; + } + } + } + } + } + String defaultPath = Settings.System.DEFAULT_NOTIFICATION_URI.getPath(); if (!notifyDisabled) { 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); + vibrateOverride = preferences.getInt("vibrate_" + dialog_id, 0); + priorityOverride = preferences.getInt("priority_" + dialog_id, 3); boolean vibrateOnlyIfSilent = false; choosenSoundPath = preferences.getString("sound_path_" + dialog_id, null); @@ -413,16 +451,16 @@ public class NotificationsController { ledColor = preferences.getInt("color_" + dialog_id, 0); } - if (priority_override != 3) { - priority = priority_override; + if (priorityOverride != 3) { + priority = priorityOverride; } if (needVibrate == 4) { vibrateOnlyIfSilent = true; needVibrate = 0; } - if (needVibrate == 2 && (vibrate_override == 1 || vibrate_override == 3 || vibrate_override == 5) || needVibrate != 2 && vibrate_override == 2 || vibrate_override != 0) { - needVibrate = vibrate_override; + if (needVibrate == 2 && (vibrateOverride == 1 || vibrateOverride == 3 || vibrateOverride == 5) || needVibrate != 2 && vibrateOverride == 2 || vibrateOverride != 0) { + needVibrate = vibrateOverride; } if (!ApplicationLoader.mainInterfacePaused) { if (!inAppSounds) { @@ -482,7 +520,7 @@ public class NotificationsController { } PendingIntent contentIntent = PendingIntent.getActivity(ApplicationLoader.applicationContext, 0, intent, PendingIntent.FLAG_ONE_SHOT); - String name = null; + String name; boolean replace = true; if ((int)dialog_id == 0 || pushDialogs.size() > 1 || AndroidUtilities.needShowPasscode(false) || UserConfig.isWaitingForPasscodeEnter) { name = LocaleController.getString("AppName", R.string.AppName); @@ -495,7 +533,7 @@ public class NotificationsController { } } - String detailText = null; + String detailText; if (pushDialogs.size() == 1) { detailText = LocaleController.formatPluralString("NewMessages", total_unread_count); } else { @@ -512,21 +550,22 @@ public class NotificationsController { .setGroupSummary(true) .setColor(0xff2ca5e0); - 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); + if (!notifyAboutLast) { + mBuilder.setPriority(NotificationCompat.PRIORITY_LOW); + } else { + 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; @@ -558,7 +597,6 @@ public class NotificationsController { } if (i == 0) { lastMessageFull = message; - //lastMessage = getStringForMessage(pushMessages.get(i), true); lastMessage = lastMessageFull; } if (pushDialogs.size() == 1) { @@ -618,18 +656,20 @@ public class NotificationsController { mBuilder.setVibrate(new long[]{0, 0}); } + showExtraNotifications(mBuilder, notifyAboutLast); notificationManager.notify(1, mBuilder.build()); if (preferences.getBoolean("EnablePebbleNotifications", false)) { sendAlertToPebble(lastMessageFull); } - showWearNotifications(notifyAboutLast); + scheduleNotificationRepeat(); } catch (Exception e) { FileLog.e("tmessages", e); } } - public void showWearNotifications(boolean notifyAboutLast) { + @SuppressLint("InlinedApi") + public void showExtraNotifications(NotificationCompat.Builder notificationBuilder, boolean notifyAboutLast) { if (Build.VERSION.SDK_INT < 19) { return; } @@ -650,16 +690,21 @@ public class NotificationsController { arrayList.add(messageObject); } - HashMap oldIds = new HashMap<>(); - oldIds.putAll(wearNoticationsIds); - wearNoticationsIds.clear(); + HashMap oldIdsWear = new HashMap<>(); + oldIdsWear.putAll(wearNotificationsIds); + wearNotificationsIds.clear(); + + HashMap oldIdsAuto = new HashMap<>(); + oldIdsAuto.putAll(autoNotificationsIds); + autoNotificationsIds.clear(); for (long dialog_id : sortedDialogs) { ArrayList messageObjects = messagesByDialogs.get(dialog_id); int max_id = messageObjects.get(0).getId(); + int max_date = messageObjects.get(0).messageOwner.date; TLRPC.Chat chat = null; TLRPC.User user = null; - String name = null; + String name; if (dialog_id > 0) { user = MessagesController.getInstance().getUser((int)dialog_id); if (user == null) { @@ -677,28 +722,56 @@ public class NotificationsController { name = ContactsController.formatName(user.first_name, user.last_name); } - Integer notificationId = oldIds.get(dialog_id); - if (notificationId == null) { - notificationId = wearNotificationId++; + Integer notificationIdWear = oldIdsWear.get(dialog_id); + if (notificationIdWear == null) { + notificationIdWear = wearNotificationId++; } else { - oldIds.remove(dialog_id); + oldIdsWear.remove(dialog_id); + } + + Integer notificationIdAuto = oldIdsAuto.get(dialog_id); + if (notificationIdAuto == null) { + notificationIdAuto = autoNotificationId++; + } else { + oldIdsAuto.remove(dialog_id); } Intent replyIntent = new Intent(ApplicationLoader.applicationContext, WearReplyReceiver.class); replyIntent.putExtra("dialog_id", dialog_id); replyIntent.putExtra("max_id", max_id); - PendingIntent replyPendingIntent = PendingIntent.getBroadcast(ApplicationLoader.applicationContext, notificationId, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT); - RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY).setLabel(LocaleController.getString("Reply", R.string.Reply)).build(); + PendingIntent replyPendingIntent = PendingIntent.getBroadcast(ApplicationLoader.applicationContext, notificationIdWear, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT); + RemoteInput remoteInputWear = new RemoteInput.Builder(EXTRA_VOICE_REPLY).setLabel(LocaleController.getString("Reply", R.string.Reply)).build(); String replyToString; if (chat != null) { replyToString = LocaleController.formatString("ReplyToGroup", R.string.ReplyToGroup, name); } else { replyToString = LocaleController.formatString("ReplyToUser", R.string.ReplyToUser, name); } - NotificationCompat.Action action = new NotificationCompat.Action.Builder(R.drawable.ic_reply_icon, replyToString, replyPendingIntent).addRemoteInput(remoteInput).build(); + NotificationCompat.Action action = new NotificationCompat.Action.Builder(R.drawable.ic_reply_icon, replyToString, replyPendingIntent).addRemoteInput(remoteInputWear).build(); + + Intent msgHeardIntent = new Intent(); + msgHeardIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); + msgHeardIntent.setAction("org.telegram.messenger.ACTION_MESSAGE_HEARD"); + msgHeardIntent.putExtra("dialog_id", dialog_id); + msgHeardIntent.putExtra("max_id", max_id); + PendingIntent msgHeardPendingIntent = PendingIntent.getBroadcast(ApplicationLoader.applicationContext, notificationIdAuto, msgHeardIntent, PendingIntent.FLAG_UPDATE_CURRENT); + + Intent msgReplyIntent = new Intent(); + msgReplyIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); + msgReplyIntent.setAction("org.telegram.messenger.ACTION_MESSAGE_REPLY"); + msgReplyIntent.putExtra("dialog_id", dialog_id); + msgReplyIntent.putExtra("max_id", max_id); + PendingIntent msgReplyPendingIntent = PendingIntent.getBroadcast(ApplicationLoader.applicationContext, notificationIdAuto, msgReplyIntent, PendingIntent.FLAG_UPDATE_CURRENT); + RemoteInput remoteInputAuto = new RemoteInput.Builder(NotificationsController.EXTRA_VOICE_REPLY).setLabel(LocaleController.getString("Reply", R.string.Reply)).build(); + + NotificationCompat.CarExtender.UnreadConversation.Builder unreadConvBuilder = new NotificationCompat.CarExtender.UnreadConversation.Builder(name) + .setReadPendingIntent(msgHeardPendingIntent) + .setReplyAction(msgReplyPendingIntent, remoteInputAuto) + .setLatestTimestamp((long) max_date * 1000); String text = ""; - for (MessageObject messageObject : messageObjects) { + for (int a = messageObjects.size() - 1; a >= 0; a--) { + MessageObject messageObject = messageObjects.get(a); String message = getStringForMessage(messageObject, false); if (message == null) { continue; @@ -712,8 +785,40 @@ public class NotificationsController { text += "\n\n"; } text += message; + + unreadConvBuilder.addMessage(message); } + + + TLRPC.FileLocation photoPath = null; + if (chat != null) { + if (chat.photo != null && chat.photo.photo_small != null && chat.photo.photo_small.volume_id != 0 && chat.photo.photo_small.local_id != 0) { + photoPath = chat.photo.photo_small; + } + } else { + if (user.photo != null && user.photo.photo_small != null && user.photo.photo_small.volume_id != 0 && user.photo.photo_small.local_id != 0) { + photoPath = user.photo.photo_small; + } + } + //notificationBuilder.extend(new NotificationCompat.CarExtender().setUnreadConversation(unreadConvBuilder.build())); + NotificationCompat.Builder builderAuto = new NotificationCompat.Builder(ApplicationLoader.applicationContext) + .setSmallIcon(R.drawable.notification) + .setColor(0xff2ca5e0) + .setGroup("messages") + .setLocalOnly(true) + //.setGroupSummary(false) + //.setCategory(NotificationCompat.CATEGORY_MESSAGE) + .extend(new NotificationCompat.CarExtender().setUnreadConversation(unreadConvBuilder.build())); + if (photoPath != null) { + BitmapDrawable img = ImageLoader.getInstance().getImageFromMemory(photoPath, null, "50_50"); + if (img != null) { + builderAuto.setLargeIcon(img.getBitmap()); + } + } + notificationManager.notify("android_auto", notificationIdAuto, builderAuto.build()); + autoNotificationsIds.put(dialog_id, notificationIdAuto); + Intent intent = new Intent(ApplicationLoader.applicationContext, LaunchActivity.class); intent.setAction("com.tmessages.openchat" + Math.random() + Integer.MAX_VALUE); intent.setFlags(32768); @@ -729,20 +834,30 @@ public class NotificationsController { .setSmallIcon(R.drawable.notification) .setGroup("messages") .setContentText(text) + .setColor(0xff2ca5e0) .setGroupSummary(false) .setContentIntent(contentIntent) .extend(new NotificationCompat.WearableExtender().addAction(action)) .setCategory(NotificationCompat.CATEGORY_MESSAGE); + if (photoPath != null) { + BitmapDrawable img = ImageLoader.getInstance().getImageFromMemory(photoPath, null, "50_50"); + if (img != null) { + builder.setLargeIcon(img.getBitmap()); + } + } 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); + notificationManager.notify(notificationIdWear, builder.build()); + wearNotificationsIds.put(dialog_id, notificationIdWear); } - for (HashMap.Entry entry : oldIds.entrySet()) { + for (HashMap.Entry entry : oldIdsAuto.entrySet()) { + notificationManager.cancel(entry.getValue()); + } + for (HashMap.Entry entry : oldIdsWear.entrySet()) { notificationManager.cancel(entry.getValue()); } } @@ -752,6 +867,14 @@ public class NotificationsController { notificationManager.cancel(1); pushMessages.clear(); pushMessagesDict.clear(); + for (HashMap.Entry entry : autoNotificationsIds.entrySet()) { + notificationManager.cancel(entry.getValue()); + } + autoNotificationsIds.clear(); + for (HashMap.Entry entry : wearNotificationsIds.entrySet()) { + notificationManager.cancel(entry.getValue()); + } + wearNotificationsIds.clear(); NotificationCenter.getInstance().postNotificationName(NotificationCenter.pushMessagesUpdated); } catch (Exception e) { FileLog.e("tmessages", e); @@ -790,6 +913,7 @@ public class NotificationsController { } popupMessages.remove(messageObject); pushMessagesDict.remove(messageObject.getId()); + delayedPushMessages.remove(messageObject); pushMessages.remove(a); a--; } @@ -821,6 +945,7 @@ public class NotificationsController { personal_count--; } pushMessages.remove(a); + delayedPushMessages.remove(messageObject); popupMessages.remove(messageObject); pushMessagesDict.remove(messageObject.getId()); a--; @@ -847,14 +972,8 @@ public class NotificationsController { try { SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Context.MODE_PRIVATE); - int notify_override = preferences.getInt("notify2_" + openned_dialog_id, 0); - if (notify_override == 3) { - int mute_until = preferences.getInt("notifyuntil_" + openned_dialog_id, 0); - if (mute_until >= ConnectionsManager.getInstance().getCurrentTime()) { - notify_override = 2; - } - } - if (notify_override == 2) { + int notifyOverride = getNotifyOverride(preferences, openned_dialog_id); + if (notifyOverride == 2) { return; } notificationsQueue.postRunnable(new Runnable() { @@ -864,56 +983,26 @@ public class NotificationsController { return; } try { - if (mediaPlayerIn == null) { - AssetFileDescriptor assetFileDescriptor = ApplicationLoader.applicationContext.getResources().openRawResourceFd(R.raw.sound_in); - if (assetFileDescriptor != null) { - mediaPlayerIn = new MediaPlayer(); - mediaPlayerIn.setAudioStreamType(AudioManager.STREAM_SYSTEM); - mediaPlayerIn.setDataSource(assetFileDescriptor.getFileDescriptor(), assetFileDescriptor.getStartOffset(), assetFileDescriptor.getLength()); - mediaPlayerIn.setLooping(false); - assetFileDescriptor.close(); - mediaPlayerIn.prepare(); - } + if (soundPool == null) { + soundPool = new SoundPool(4, AudioManager.STREAM_SYSTEM, 0); + soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() { + @Override + public void onLoadComplete(SoundPool soundPool, int sampleId, int status) { + if (status == 0) { + soundPool.play(sampleId, 1.0f, 1.0f, 1, 0, 1.0f); + } + } + }); } - mediaPlayerIn.start(); + if (soundIn == 0) { + soundIn = soundPool.load(ApplicationLoader.applicationContext, R.raw.sound_in, 1); + } + soundPool.play(soundIn, 1.0f, 1.0f, 1, 0, 1.0f); } catch (Exception e) { FileLog.e("tmessages", e); } } }); - /*String choosenSoundPath = null; - String defaultPath = Settings.System.DEFAULT_NOTIFICATION_URI.getPath(); - - choosenSoundPath = preferences.getString("sound_path_" + openned_dialog_id, null); - boolean isChat = (int)(openned_dialog_id) < 0; - if (isChat) { - if (choosenSoundPath != null && choosenSoundPath.equals(defaultPath)) { - choosenSoundPath = null; - } else if (choosenSoundPath == null) { - choosenSoundPath = preferences.getString("GroupSoundPath", defaultPath); - } - } else { - if (choosenSoundPath != null && choosenSoundPath.equals(defaultPath)) { - choosenSoundPath = null; - } else if (choosenSoundPath == null) { - choosenSoundPath = preferences.getString("GlobalSoundPath", defaultPath); - } - } - - if (choosenSoundPath != null && !choosenSoundPath.equals("NoSound")) { - if (lastMediaPlayerUri == null || !choosenSoundPath.equals(lastMediaPlayerUri)) { - lastMediaPlayerUri = choosenSoundPath; - mediaPlayer.reset(); - mediaPlayer.setAudioStreamType(AudioManager.STREAM_NOTIFICATION); - if (choosenSoundPath.equals(defaultPath)) { - mediaPlayer.setDataSource(ApplicationLoader.applicationContext, Settings.System.DEFAULT_NOTIFICATION_URI); - } else { - mediaPlayer.setDataSource(ApplicationLoader.applicationContext, Uri.parse(choosenSoundPath)); - } - mediaPlayer.prepare(); - } - mediaPlayer.start(); - }*/ } catch (Exception e) { FileLog.e("tmessages", e); } @@ -934,18 +1023,25 @@ public class NotificationsController { @Override public void run() { try { - if (mediaPlayerOut == null) { - AssetFileDescriptor assetFileDescriptor = ApplicationLoader.applicationContext.getResources().openRawResourceFd(R.raw.sound_out); - if (assetFileDescriptor != null) { - mediaPlayerOut = new MediaPlayer(); - mediaPlayerOut.setAudioStreamType(AudioManager.STREAM_SYSTEM); - mediaPlayerOut.setDataSource(assetFileDescriptor.getFileDescriptor(), assetFileDescriptor.getStartOffset(), assetFileDescriptor.getLength()); - mediaPlayerOut.setLooping(false); - assetFileDescriptor.close(); - mediaPlayerOut.prepare(); - } + if (lastSoundOutPlay > System.currentTimeMillis() - 100) { + return; } - mediaPlayerOut.start(); + lastSoundOutPlay = System.currentTimeMillis(); + if (soundPool == null) { + soundPool = new SoundPool(4, AudioManager.STREAM_SYSTEM, 0); + soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() { + @Override + public void onLoadComplete(SoundPool soundPool, int sampleId, int status) { + if (status == 0) { + soundPool.play(sampleId, 1.0f, 1.0f, 1, 0, 1.0f); + } + } + }); + } + if (soundOut == 0) { + soundOut = soundPool.load(ApplicationLoader.applicationContext, R.raw.sound_out, 1); + } + soundPool.play(soundOut, 1.0f, 1.0f, 1, 0, 1.0f); } catch (Exception e) { FileLog.e("tmessages", e); } @@ -953,6 +1049,17 @@ public class NotificationsController { }); } + private int getNotifyOverride(SharedPreferences preferences, long dialog_id) { + int notifyOverride = preferences.getInt("notify2_" + dialog_id, 0); + if (notifyOverride == 3) { + int muteUntil = preferences.getInt("notifyuntil_" + dialog_id, 0); + if (muteUntil >= ConnectionsManager.getInstance().getCurrentTime()) { + notifyOverride = 2; + } + } + return notifyOverride; + } + public void processNewMessages(ArrayList messageObjects, boolean isLast) { if (messageObjects.isEmpty()) { return; @@ -986,20 +1093,15 @@ public class NotificationsController { boolean isChat = (int)dialog_id < 0; popup = (int)dialog_id == 0 ? 0 : preferences.getInt(isChat ? "popupGroup" : "popupAll", 0); if (value == null) { - int notify_override = preferences.getInt("notify2_" + dialog_id, 0); - if (notify_override == 3) { - int mute_until = preferences.getInt("notifyuntil_" + dialog_id, 0); - if (mute_until >= ConnectionsManager.getInstance().getCurrentTime()) { - notify_override = 2; - } - } - value = !(notify_override == 2 || (!preferences.getBoolean("EnableAll", true) || isChat && !preferences.getBoolean("EnableGroup", true)) && notify_override == 0); + int notifyOverride = getNotifyOverride(preferences, dialog_id); + value = !(notifyOverride == 2 || (!preferences.getBoolean("EnableAll", true) || isChat && !preferences.getBoolean("EnableGroup", true)) && notifyOverride == 0); settingsCache.put(dialog_id, value); } if (value) { if (popup != 0) { popupMessages.add(0, messageObject); } + delayedPushMessages.add(messageObject); pushMessages.add(0, messageObject); pushMessagesDict.put(messageObject.getId(), messageObject); if (original_dialog_id != dialog_id) { @@ -1030,24 +1132,22 @@ public class NotificationsController { for (HashMap.Entry entry : dialogsToUpdate.entrySet()) { long dialog_id = entry.getKey(); - int notify_override = preferences.getInt("notify2_" + dialog_id, 0); - if (notify_override == 3) { - int mute_until = preferences.getInt("notifyuntil_" + dialog_id, 0); - if (mute_until >= ConnectionsManager.getInstance().getCurrentTime()) { - notify_override = 2; - } - } + int notifyOverride = getNotifyOverride(preferences, dialog_id); if (notifyCheck) { Integer override = pushDialogsOverrideMention.get(dialog_id); if (override != null && override == 1) { pushDialogsOverrideMention.put(dialog_id, 0); - notify_override = 1; + notifyOverride = 1; } } - boolean canAddValue = !(notify_override == 2 || (!preferences.getBoolean("EnableAll", true) || ((int)dialog_id < 0) && !preferences.getBoolean("EnableGroup", true)) && notify_override == 0); + boolean canAddValue = !(notifyOverride == 2 || (!preferences.getBoolean("EnableAll", true) || ((int)dialog_id < 0) && !preferences.getBoolean("EnableGroup", true)) && notifyOverride == 0); Integer currentCount = pushDialogs.get(dialog_id); Integer newCount = entry.getValue(); + if (newCount == 0) { + smartNotificationsDialogs.remove(dialog_id); + } + if (newCount < 0) { if (currentCount == null) { continue; @@ -1070,6 +1170,7 @@ public class NotificationsController { } pushMessages.remove(a); a--; + delayedPushMessages.remove(messageObject); pushMessagesDict.remove(messageObject.getId()); popupMessages.remove(messageObject); } @@ -1079,16 +1180,13 @@ public class NotificationsController { pushDialogs.put(dialog_id, newCount); } } - /*if (old_unread_count != total_unread_count) { TODO - if (lastOnlineFromOtherDevice > ConnectionsManager.getInstance().getCurrentTime()) { - showOrUpdateNotification(false); - scheduleNotificationDelay(true); - } else { - showOrUpdateNotification(notifyCheck); - } - }*/ if (old_unread_count != total_unread_count) { - showOrUpdateNotification(notifyCheck); + if (!notifyCheck) { + delayedPushMessages.clear(); + showOrUpdateNotification(notifyCheck); + } else { + scheduleNotificationDelay(lastOnlineFromOtherDevice > ConnectionsManager.getInstance().getCurrentTime()); + } } notifyCheck = false; if (preferences.getBoolean("badgeNumber", true)) { @@ -1125,14 +1223,8 @@ public class NotificationsController { } Boolean value = settingsCache.get(dialog_id); if (value == null) { - int notify_override = preferences.getInt("notify2_" + dialog_id, 0); - if (notify_override == 3) { - int mute_until = preferences.getInt("notifyuntil_" + dialog_id, 0); - if (mute_until >= ConnectionsManager.getInstance().getCurrentTime()) { - notify_override = 2; - } - } - value = !(notify_override == 2 || (!preferences.getBoolean("EnableAll", true) || ((int) dialog_id < 0) && !preferences.getBoolean("EnableGroup", true)) && notify_override == 0); + int notifyOverride = getNotifyOverride(preferences, dialog_id); + value = !(notifyOverride == 2 || (!preferences.getBoolean("EnableAll", true) || ((int) dialog_id < 0) && !preferences.getBoolean("EnableGroup", true)) && notifyOverride == 0); settingsCache.put(dialog_id, value); } if (!value || dialog_id == openned_dialog_id && ApplicationLoader.isScreenOn) { @@ -1149,19 +1241,13 @@ public class NotificationsController { long dialog_id = entry.getKey(); Boolean value = settingsCache.get(dialog_id); if (value == null) { - int notify_override = preferences.getInt("notify2_" + dialog_id, 0); - if (notify_override == 3) { - int mute_until = preferences.getInt("notifyuntil_" + dialog_id, 0); - if (mute_until >= ConnectionsManager.getInstance().getCurrentTime()) { - notify_override = 2; - } - } + int notifyOverride = getNotifyOverride(preferences, dialog_id); Integer override = pushDialogsOverrideMention.get(dialog_id); if (override != null && override == 1) { pushDialogsOverrideMention.put(dialog_id, 0); - notify_override = 1; + notifyOverride = 1; } - value = !(notify_override == 2 || (!preferences.getBoolean("EnableAll", true) || ((int) dialog_id < 0) && !preferences.getBoolean("EnableGroup", true)) && notify_override == 0); + value = !(notifyOverride == 2 || (!preferences.getBoolean("EnableAll", true) || ((int) dialog_id < 0) && !preferences.getBoolean("EnableGroup", true)) && notifyOverride == 0); settingsCache.put(dialog_id, value); } if (!value) { @@ -1190,6 +1276,10 @@ public class NotificationsController { notificationsQueue.postRunnable(new Runnable() { @Override public void run() { + if (lastBadgeCount == count) { + return; + } + lastBadgeCount = count; try { ContentValues cv = new ContentValues(); cv.put("tag", "org.telegram.messenger/org.telegram.ui.LaunchActivity"); @@ -1199,15 +1289,24 @@ public class NotificationsController { //ignore } try { - String launcherClassName = getLauncherClassName(context); + launcherClassName = getLauncherClassName(context); if (launcherClassName == null) { return; } - Intent intent = new Intent("android.intent.action.BADGE_COUNT_UPDATE"); - intent.putExtra("badge_count", count); - intent.putExtra("badge_count_package_name", context.getPackageName()); - intent.putExtra("badge_count_class_name", launcherClassName); - context.sendBroadcast(intent); + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + try { + Intent intent = new Intent("android.intent.action.BADGE_COUNT_UPDATE"); + intent.putExtra("badge_count", count); + intent.putExtra("badge_count_package_name", context.getPackageName()); + intent.putExtra("badge_count_class_name", launcherClassName); + context.sendBroadcast(intent); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + }); } catch (Throwable e) { FileLog.e("tmessages", e); } diff --git a/TMessagesProj/src/main/java/org/telegram/android/ScreenReceiver.java b/TMessagesProj/src/main/java/org/telegram/android/ScreenReceiver.java index d7824f18b..90c1e2bf1 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/ScreenReceiver.java +++ b/TMessagesProj/src/main/java/org/telegram/android/ScreenReceiver.java @@ -17,6 +17,7 @@ import org.telegram.messenger.FileLog; import org.telegram.messenger.ApplicationLoader; public class ScreenReceiver extends BroadcastReceiver { + @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { diff --git a/TMessagesProj/src/main/java/org/telegram/android/SecretChatHelper.java b/TMessagesProj/src/main/java/org/telegram/android/SecretChatHelper.java index 6a34ea267..5d793ea3d 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/SecretChatHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/android/SecretChatHelper.java @@ -48,6 +48,7 @@ public class SecretChatHelper { private boolean startingSecretChat = false; private static volatile SecretChatHelper Instance = null; + public static SecretChatHelper getInstance() { SecretChatHelper localInstance = Instance; if (localInstance == null) { @@ -87,7 +88,7 @@ public class SecretChatHelper { 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.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()) { @@ -114,7 +115,7 @@ public class SecretChatHelper { if (!(encryptedChat instanceof TLRPC.TL_encryptedChat)) { return; } - TLRPC.TL_decryptedMessageService reqSend = null; + TLRPC.TL_decryptedMessageService reqSend; if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) { reqSend = new TLRPC.TL_decryptedMessageService(); } else { @@ -123,7 +124,7 @@ public class SecretChatHelper { Utilities.random.nextBytes(reqSend.random_bytes); } - TLRPC.Message message = null; + TLRPC.Message message; if (resendMessage != null) { message = resendMessage; @@ -140,7 +141,7 @@ public class SecretChatHelper { protected void processUpdateEncryption(TLRPC.TL_updateEncryption update, ConcurrentHashMap usersDict) { final TLRPC.EncryptedChat newChat = update.chat; - long dialog_id = ((long)newChat.id) << 32; + long dialog_id = ((long) newChat.id) << 32; TLRPC.EncryptedChat existingChat = MessagesController.getInstance().getEncryptedChatDB(newChat.id); if (newChat instanceof TLRPC.TL_encryptedChatRequested && existingChat == null) { @@ -219,7 +220,7 @@ public class SecretChatHelper { if (!(encryptedChat instanceof TLRPC.TL_encryptedChat)) { return; } - TLRPC.TL_decryptedMessageService reqSend = null; + TLRPC.TL_decryptedMessageService reqSend; if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) { reqSend = new TLRPC.TL_decryptedMessageService(); } else { @@ -228,7 +229,7 @@ public class SecretChatHelper { Utilities.random.nextBytes(reqSend.random_bytes); } - TLRPC.Message message = null; + TLRPC.Message message; if (resendMessage != null) { message = resendMessage; @@ -247,7 +248,7 @@ public class SecretChatHelper { if (!(encryptedChat instanceof TLRPC.TL_encryptedChat)) { return; } - TLRPC.TL_decryptedMessageService reqSend = null; + TLRPC.TL_decryptedMessageService reqSend; if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) { reqSend = new TLRPC.TL_decryptedMessageService(); } else { @@ -256,7 +257,7 @@ public class SecretChatHelper { Utilities.random.nextBytes(reqSend.random_bytes); } - TLRPC.Message message = null; + TLRPC.Message message; if (resendMessage != null) { message = resendMessage; @@ -278,7 +279,7 @@ public class SecretChatHelper { return; } sendingNotifyLayer.add(encryptedChat.id); - TLRPC.TL_decryptedMessageService reqSend = null; + TLRPC.TL_decryptedMessageService reqSend; if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) { reqSend = new TLRPC.TL_decryptedMessageService(); } else { @@ -287,7 +288,7 @@ public class SecretChatHelper { Utilities.random.nextBytes(reqSend.random_bytes); } - TLRPC.Message message = null; + TLRPC.Message message; if (resendMessage != null) { message = resendMessage; @@ -307,7 +308,7 @@ public class SecretChatHelper { return; } - TLRPC.TL_decryptedMessageService reqSend = null; + TLRPC.TL_decryptedMessageService reqSend; if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) { reqSend = new TLRPC.TL_decryptedMessageService(); } else { @@ -316,7 +317,7 @@ public class SecretChatHelper { Utilities.random.nextBytes(reqSend.random_bytes); } - TLRPC.Message message = null; + TLRPC.Message message; if (resendMessage != null) { message = resendMessage; @@ -338,7 +339,7 @@ public class SecretChatHelper { return; } - TLRPC.TL_decryptedMessageService reqSend = null; + TLRPC.TL_decryptedMessageService reqSend; if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) { reqSend = new TLRPC.TL_decryptedMessageService(); } else { @@ -347,7 +348,7 @@ public class SecretChatHelper { Utilities.random.nextBytes(reqSend.random_bytes); } - TLRPC.Message message = null; + TLRPC.Message message; if (resendMessage != null) { message = resendMessage; @@ -370,7 +371,7 @@ public class SecretChatHelper { return; } - TLRPC.TL_decryptedMessageService reqSend = null; + TLRPC.TL_decryptedMessageService reqSend; if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) { reqSend = new TLRPC.TL_decryptedMessageService(); } else { @@ -379,7 +380,7 @@ public class SecretChatHelper { Utilities.random.nextBytes(reqSend.random_bytes); } - TLRPC.Message message = null; + TLRPC.Message message; if (resendMessage != null) { message = resendMessage; @@ -401,7 +402,7 @@ public class SecretChatHelper { return; } - TLRPC.TL_decryptedMessageService reqSend = null; + TLRPC.TL_decryptedMessageService reqSend; if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) { reqSend = new TLRPC.TL_decryptedMessageService(); } else { @@ -410,7 +411,7 @@ public class SecretChatHelper { Utilities.random.nextBytes(reqSend.random_bytes); } - TLRPC.Message message = null; + TLRPC.Message message; if (resendMessage != null) { message = resendMessage; @@ -431,7 +432,7 @@ public class SecretChatHelper { return; } - TLRPC.TL_decryptedMessageService reqSend = null; + TLRPC.TL_decryptedMessageService reqSend; if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) { reqSend = new TLRPC.TL_decryptedMessageService(); } else { @@ -440,7 +441,7 @@ public class SecretChatHelper { Utilities.random.nextBytes(reqSend.random_bytes); } - TLRPC.Message message = null; + TLRPC.Message message; if (resendMessage != null) { message = resendMessage; @@ -459,7 +460,7 @@ public class SecretChatHelper { return; } - TLRPC.TL_decryptedMessageService reqSend = null; + TLRPC.TL_decryptedMessageService reqSend; if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) { reqSend = new TLRPC.TL_decryptedMessageService(); } else { @@ -468,7 +469,7 @@ public class SecretChatHelper { Utilities.random.nextBytes(reqSend.random_bytes); } - TLRPC.Message message = null; + TLRPC.Message message; if (resendMessage != null) { message = resendMessage; @@ -495,7 +496,7 @@ public class SecretChatHelper { return; } - TLRPC.TL_decryptedMessageService reqSend = null; + TLRPC.TL_decryptedMessageService reqSend; if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) { reqSend = new TLRPC.TL_decryptedMessageService(); } else { @@ -504,7 +505,7 @@ public class SecretChatHelper { Utilities.random.nextBytes(reqSend.random_bytes); } - TLRPC.Message message = null; + TLRPC.Message message; if (resendMessage != null) { message = resendMessage; @@ -542,7 +543,7 @@ public class SecretChatHelper { 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); + ImageLoader.getInstance().replaceImageInCache(fileName, fileName2, size.location); ArrayList arr = new ArrayList<>(); arr.add(newMsg); MessagesStorage.getInstance().putMessages(arr, false, true, false, 0); @@ -557,7 +558,7 @@ public class SecretChatHelper { newMsg.media.video.w = video.w; newMsg.media.video.h = video.h; newMsg.media.video.date = video.date; - newMsg.media.video.caption = ""; + newMsg.media.caption = video.caption != null ? video.caption : ""; newMsg.media.video.user_id = video.user_id; newMsg.media.video.size = file.size; newMsg.media.video.id = file.id; @@ -565,6 +566,7 @@ public class SecretChatHelper { newMsg.media.video.key = decryptedMessage.media.key; newMsg.media.video.iv = decryptedMessage.media.iv; newMsg.media.video.mime_type = video.mime_type; + newMsg.media.video.caption = video.caption != null ? video.caption : ""; if (newMsg.attachPath != null && newMsg.attachPath.startsWith(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE).getAbsolutePath())) { File cacheFile = new File(newMsg.attachPath); @@ -653,7 +655,7 @@ public class SecretChatHelper { Utilities.stageQueue.postRunnable(new Runnable() { @Override public void run() { - TLObject toEncryptObject = null; + TLObject toEncryptObject; if (AndroidUtilities.getPeerLayerVersion(chat.layer) >= 17) { TLRPC.TL_decryptedMessageLayer layer = new TLRPC.TL_decryptedMessageLayer(); int myLayer = Math.max(17, AndroidUtilities.getMyLayerVersion(chat.layer)); @@ -709,7 +711,7 @@ public class SecretChatHelper { byte[] messageKey = new byte[16]; System.arraycopy(messageKeyFull, messageKeyFull.length - 16, messageKey, 0, 16); - MessageKeyData keyData = Utilities.generateMessageKeyData(chat.auth_key, messageKey, false); + MessageKeyData keyData = MessageKeyData.generateMessageKeyData(chat.auth_key, messageKey, false); len = toEncrypt.length(); int extraLen = len % 16 != 0 ? 16 - len % 16 : 0; @@ -733,7 +735,7 @@ public class SecretChatHelper { BuffersStorage.getInstance().reuseFreeBuffer(dataForEncryption); data.position(0); - TLObject reqToSend = null; + TLObject reqToSend; if (encryptedFile == null) { if (req instanceof TLRPC.TL_decryptedMessageService) { @@ -769,7 +771,7 @@ public class SecretChatHelper { if (error == null) { if (req.action instanceof TLRPC.TL_decryptedMessageActionNotifyLayer) { TLRPC.EncryptedChat currentChat = MessagesController.getInstance().getEncryptedChat(chat.id); - sendingNotifyLayer.remove((Integer)currentChat.id); + sendingNotifyLayer.remove((Integer) currentChat.id); currentChat.layer = AndroidUtilities.setMyLayerVersion(currentChat.layer, CURRENT_SECRET_CHAT_LAYER); MessagesStorage.getInstance().updateEncryptedChatLayer(currentChat); } @@ -856,8 +858,8 @@ public class SecretChatHelper { } if (object instanceof TLRPC.TL_decryptedMessage) { - TLRPC.TL_decryptedMessage decryptedMessage = (TLRPC.TL_decryptedMessage)object; - TLRPC.TL_message newMessage = null; + TLRPC.TL_decryptedMessage decryptedMessage = (TLRPC.TL_decryptedMessage) object; + TLRPC.TL_message newMessage; if (AndroidUtilities.getPeerLayerVersion(chat.layer) >= 17) { newMessage = new TLRPC.TL_message_secret(); newMessage.ttl = decryptedMessage.ttl; @@ -874,7 +876,7 @@ public class SecretChatHelper { newMessage.random_id = random_id; newMessage.to_id.user_id = UserConfig.getClientUserId(); newMessage.flags = TLRPC.MESSAGE_FLAG_UNREAD; - newMessage.dialog_id = ((long)chat.id) << 32; + newMessage.dialog_id = ((long) chat.id) << 32; if (decryptedMessage.media instanceof TLRPC.TL_decryptedMessageMediaEmpty) { newMessage.media = new TLRPC.TL_messageMediaEmpty(); } else if (decryptedMessage.media instanceof TLRPC.TL_decryptedMessageMediaContact) { @@ -893,16 +895,17 @@ public class SecretChatHelper { return null; } newMessage.media = new TLRPC.TL_messageMediaPhoto(); + newMessage.media.caption = ""; newMessage.media.photo = new TLRPC.TL_photo(); newMessage.media.photo.user_id = newMessage.from_id; newMessage.media.photo.date = newMessage.date; - newMessage.media.photo.caption = ""; newMessage.media.photo.geo = new TLRPC.TL_geoPointEmpty(); - if (decryptedMessage.media.thumb.length != 0 && decryptedMessage.media.thumb.length <= 6000 && decryptedMessage.media.thumb_w <= 100 && decryptedMessage.media.thumb_h <= 100) { + byte[] thumb = ((TLRPC.TL_decryptedMessageMediaPhoto) decryptedMessage.media).thumb; + if (thumb != null && thumb.length != 0 && thumb.length <= 6000 && decryptedMessage.media.thumb_w <= 100 && decryptedMessage.media.thumb_h <= 100) { TLRPC.TL_photoCachedSize small = new TLRPC.TL_photoCachedSize(); small.w = decryptedMessage.media.thumb_w; small.h = decryptedMessage.media.thumb_h; - small.bytes = decryptedMessage.media.thumb; + small.bytes = thumb; small.type = "s"; small.location = new TLRPC.TL_fileLocationUnavailable(); newMessage.media.photo.sizes.add(small); @@ -926,10 +929,12 @@ public class SecretChatHelper { return null; } newMessage.media = new TLRPC.TL_messageMediaVideo(); + newMessage.media.caption = ""; newMessage.media.video = new TLRPC.TL_videoEncrypted(); - if (decryptedMessage.media.thumb.length != 0 && decryptedMessage.media.thumb.length <= 6000 && decryptedMessage.media.thumb_w <= 100 && decryptedMessage.media.thumb_h <= 100) { + byte[] thumb = ((TLRPC.TL_decryptedMessageMediaVideo) decryptedMessage.media).thumb; + if (thumb != null && thumb.length != 0 && thumb.length <= 6000 && decryptedMessage.media.thumb_w <= 100 && decryptedMessage.media.thumb_h <= 100) { newMessage.media.video.thumb = new TLRPC.TL_photoCachedSize(); - newMessage.media.video.thumb.bytes = decryptedMessage.media.thumb; + newMessage.media.video.thumb.bytes = thumb; newMessage.media.video.thumb.w = decryptedMessage.media.thumb_w; newMessage.media.video.thumb.h = decryptedMessage.media.thumb_h; newMessage.media.video.thumb.type = "s"; @@ -943,7 +948,6 @@ public class SecretChatHelper { newMessage.media.video.w = decryptedMessage.media.w; newMessage.media.video.h = decryptedMessage.media.h; newMessage.media.video.date = date; - newMessage.media.video.caption = ""; newMessage.media.video.user_id = from_id; newMessage.media.video.size = file.size; newMessage.media.video.id = file.id; @@ -951,6 +955,7 @@ public class SecretChatHelper { newMessage.media.video.key = decryptedMessage.media.key; newMessage.media.video.iv = decryptedMessage.media.iv; newMessage.media.video.mime_type = decryptedMessage.media.mime_type; + newMessage.media.video.caption = ""; if (newMessage.ttl != 0) { newMessage.ttl = Math.max(newMessage.media.video.duration + 1, newMessage.ttl); } @@ -973,9 +978,10 @@ public class SecretChatHelper { newMessage.media.document.size = file.size; newMessage.media.document.key = decryptedMessage.media.key; newMessage.media.document.iv = decryptedMessage.media.iv; - if (decryptedMessage.media.thumb.length != 0 && decryptedMessage.media.thumb.length <= 6000 && decryptedMessage.media.thumb_w <= 100 && decryptedMessage.media.thumb_h <= 100) { + byte[] thumb = ((TLRPC.TL_decryptedMessageMediaDocument) decryptedMessage.media).thumb; + if (thumb != null && thumb.length != 0 && thumb.length <= 6000 && decryptedMessage.media.thumb_w <= 100 && decryptedMessage.media.thumb_h <= 100) { newMessage.media.document.thumb = new TLRPC.TL_photoCachedSize(); - newMessage.media.document.thumb.bytes = decryptedMessage.media.thumb; + newMessage.media.document.thumb.bytes = thumb; newMessage.media.document.thumb.w = decryptedMessage.media.thumb_w; newMessage.media.document.thumb.h = decryptedMessage.media.thumb_h; newMessage.media.document.thumb.type = "s"; @@ -995,7 +1001,7 @@ public class SecretChatHelper { newMessage.media.document.mime_type = decryptedMessage.media.mime_type; newMessage.media.document.dc_id = decryptedMessage.media.dc_id; newMessage.media.document.size = decryptedMessage.media.size; - newMessage.media.document.thumb = decryptedMessage.media.thumbImage; + newMessage.media.document.thumb = ((TLRPC.TL_decryptedMessageMediaExternalDocument) decryptedMessage.media).thumb; } else if (decryptedMessage.media instanceof TLRPC.TL_decryptedMessageMediaAudio) { if (decryptedMessage.media.key == null || decryptedMessage.media.key.length != 32 || decryptedMessage.media.iv == null || decryptedMessage.media.iv.length != 32) { return null; @@ -1023,7 +1029,7 @@ public class SecretChatHelper { } return newMessage; } else if (object instanceof TLRPC.TL_decryptedMessageService) { - final TLRPC.TL_decryptedMessageService serviceMessage = (TLRPC.TL_decryptedMessageService)object; + final TLRPC.TL_decryptedMessageService serviceMessage = (TLRPC.TL_decryptedMessageService) object; if (serviceMessage.action instanceof TLRPC.TL_decryptedMessageActionSetMessageTTL || serviceMessage.action instanceof TLRPC.TL_decryptedMessageActionScreenshotMessages) { TLRPC.TL_messageService newMessage = new TLRPC.TL_messageService(); if (serviceMessage.action instanceof TLRPC.TL_decryptedMessageActionSetMessageTTL) { @@ -1045,10 +1051,10 @@ public class SecretChatHelper { newMessage.from_id = from_id; newMessage.to_id = new TLRPC.TL_peerUser(); newMessage.to_id.user_id = UserConfig.getClientUserId(); - newMessage.dialog_id = ((long)chat.id) << 32; + newMessage.dialog_id = ((long) chat.id) << 32; return newMessage; } else if (serviceMessage.action instanceof TLRPC.TL_decryptedMessageActionFlushHistory) { - final long did = ((long)chat.id) << 32; + final long did = ((long) chat.id) << 32; AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { @@ -1300,7 +1306,7 @@ public class SecretChatHelper { ByteBufferDesc is = BuffersStorage.getInstance().getFreeBuffer(message.bytes.length); is.writeRaw(message.bytes); is.position(0); - long fingerprint = is.readInt64(); + long fingerprint = is.readInt64(false); byte[] keyToDecrypt = null; boolean new_key_used = false; if (chat.key_fingerprint == fingerprint) { @@ -1311,12 +1317,12 @@ public class SecretChatHelper { } if (keyToDecrypt != null) { - byte[] messageKey = is.readData(16); - MessageKeyData keyData = Utilities.generateMessageKeyData(keyToDecrypt, messageKey, false); + byte[] messageKey = is.readData(16, false); + MessageKeyData keyData = MessageKeyData.generateMessageKeyData(keyToDecrypt, messageKey, false); Utilities.aesIgeEncryption(is.buffer, keyData.aesKey, keyData.aesIv, false, false, 24, is.limit() - 24); - int len = is.readInt32(); + int len = is.readInt32(false); if (len < 0 || len > is.limit() - 28) { return null; } @@ -1325,13 +1331,14 @@ public class SecretChatHelper { return null; } - TLObject object = TLClassStore.Instance().TLdeserialize(is, is.readInt32()); + TLObject object = TLClassStore.Instance().TLdeserialize(is, is.readInt32(false), false); + BuffersStorage.getInstance().reuseFreeBuffer(is); if (!new_key_used && AndroidUtilities.getPeerLayerVersion(chat.layer) >= 20) { chat.key_use_count_in++; } if (object instanceof TLRPC.TL_decryptedMessageLayer) { - final TLRPC.TL_decryptedMessageLayer layer = (TLRPC.TL_decryptedMessageLayer)object; + final TLRPC.TL_decryptedMessageLayer layer = (TLRPC.TL_decryptedMessageLayer) object; if (chat.seq_in == 0 && chat.seq_out == 0) { if (chat.admin_id == UserConfig.getClientUserId()) { chat.seq_out = 1; diff --git a/TMessagesProj/src/main/java/org/telegram/android/SendMessagesHelper.java b/TMessagesProj/src/main/java/org/telegram/android/SendMessagesHelper.java index 35e6bedea..3aa324556 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/SendMessagesHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/android/SendMessagesHelper.java @@ -44,7 +44,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter private HashMap unsentMessages = new HashMap<>(); private HashMap sendingMessages = new HashMap<>(); - private class DelayedMessage { + protected class DelayedMessage { public TLObject sendRequest; public TLRPC.TL_decryptedMessage sendEncryptedRequest; public int type; @@ -56,9 +56,11 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter public String httpLocation; public MessageObject obj; public TLRPC.EncryptedChat encryptedChat; + public VideoEditedInfo videoEditedInfo; } private static volatile SendMessagesHelper Instance = null; + public static SendMessagesHelper getInstance() { SendMessagesHelper localInstance = Instance; if (localInstance == null) { @@ -94,113 +96,100 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } @Override - public void didReceivedNotification(int id, Object... args) { + public void didReceivedNotification(int id, final Object... args) { if (id == NotificationCenter.FileDidUpload) { - final String location = (String)args[0]; - final TLRPC.InputFile file = (TLRPC.InputFile)args[1]; - final TLRPC.InputEncryptedFile encryptedFile = (TLRPC.InputEncryptedFile)args[2]; + final String location = (String) args[0]; + final TLRPC.InputFile file = (TLRPC.InputFile) args[1]; + final TLRPC.InputEncryptedFile encryptedFile = (TLRPC.InputEncryptedFile) args[2]; + ArrayList arr = delayedMessages.get(location); + if (arr != null) { + for (int a = 0; a < arr.size(); a++) { + DelayedMessage message = arr.get(a); + TLRPC.InputMedia media = null; + if (message.sendRequest instanceof TLRPC.TL_messages_sendMedia) { + media = ((TLRPC.TL_messages_sendMedia) message.sendRequest).media; + } else if (message.sendRequest instanceof TLRPC.TL_messages_sendBroadcast) { + media = ((TLRPC.TL_messages_sendBroadcast) message.sendRequest).media; + } - AndroidUtilities.runOnUIThread(new Runnable() { - @Override - public void run() { - ArrayList arr = delayedMessages.get(location); - if (arr != null) { - for (int a = 0; a < arr.size(); a++) { - DelayedMessage message = arr.get(a); - TLRPC.InputMedia media = null; - if (message.sendRequest instanceof TLRPC.TL_messages_sendMedia) { - media = ((TLRPC.TL_messages_sendMedia)message.sendRequest).media; - } else if (message.sendRequest instanceof TLRPC.TL_messages_sendBroadcast) { - media = ((TLRPC.TL_messages_sendBroadcast)message.sendRequest).media; - } - - if (file != null && media != null) { - if (message.type == 0) { - media.file = file; - performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath); - } else if (message.type == 1) { - if (media.file == null) { - media.file = file; - if (media.thumb == null && message.location != null) { - performSendDelayedMessage(message); - } else { - performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath); - } - } else { - media.thumb = file; - performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath); - } - } else if (message.type == 2) { - if (media.file == null) { - media.file = file; - if (media.thumb == null && message.location != null) { - performSendDelayedMessage(message); - } else { - performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath); - } - } else { - media.thumb = file; - performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath); - } - } else if (message.type == 3) { - media.file = file; + if (file != null && media != null) { + if (message.type == 0) { + media.file = file; + performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath); + } else if (message.type == 1) { + if (media.file == null) { + media.file = file; + if (media.thumb == null && message.location != null) { + performSendDelayedMessage(message); + } else { performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath); } - arr.remove(a); - a--; - } else if (encryptedFile != null && message.sendEncryptedRequest != null) { - message.sendEncryptedRequest.media.key = encryptedFile.key; - message.sendEncryptedRequest.media.iv = encryptedFile.iv; - SecretChatHelper.getInstance().performSendEncryptedRequest(message.sendEncryptedRequest, message.obj.messageOwner, message.encryptedChat, encryptedFile, message.originalPath); - arr.remove(a); - a--; + } else { + media.thumb = file; + performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath); } + } else if (message.type == 2) { + if (media.file == null) { + media.file = file; + if (media.thumb == null && message.location != null) { + performSendDelayedMessage(message); + } else { + performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath); + } + } else { + media.thumb = file; + performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath); + } + } else if (message.type == 3) { + media.file = file; + performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath); } - if (arr.isEmpty()) { - delayedMessages.remove(location); - } + arr.remove(a); + a--; + } else if (encryptedFile != null && message.sendEncryptedRequest != null) { + message.sendEncryptedRequest.media.key = (byte[]) args[3]; + message.sendEncryptedRequest.media.iv = (byte[]) args[4]; + SecretChatHelper.getInstance().performSendEncryptedRequest(message.sendEncryptedRequest, message.obj.messageOwner, message.encryptedChat, encryptedFile, message.originalPath); + arr.remove(a); + a--; } } - }); + if (arr.isEmpty()) { + delayedMessages.remove(location); + } + } } else if (id == NotificationCenter.FileDidFailUpload) { final String location = (String) args[0]; final boolean enc = (Boolean) args[1]; - - AndroidUtilities.runOnUIThread(new Runnable() { - @Override - public void run() { - ArrayList arr = delayedMessages.get(location); - if (arr != null) { - for (int a = 0; a < arr.size(); a++) { - DelayedMessage obj = arr.get(a); - if (enc && obj.sendEncryptedRequest != null || !enc && obj.sendRequest != null) { - MessagesStorage.getInstance().markMessageAsSendError(obj.obj.getId()); - obj.obj.messageOwner.send_state = MessageObject.MESSAGE_SEND_STATE_SEND_ERROR; - arr.remove(a); - a--; - NotificationCenter.getInstance().postNotificationName(NotificationCenter.messageSendError, obj.obj.getId()); - processSentMessage(obj.obj.getId()); - } - } - if (arr.isEmpty()) { - delayedMessages.remove(location); - } + ArrayList arr = delayedMessages.get(location); + if (arr != null) { + for (int a = 0; a < arr.size(); a++) { + DelayedMessage obj = arr.get(a); + if (enc && obj.sendEncryptedRequest != null || !enc && obj.sendRequest != null) { + MessagesStorage.getInstance().markMessageAsSendError(obj.obj.getId()); + obj.obj.messageOwner.send_state = MessageObject.MESSAGE_SEND_STATE_SEND_ERROR; + arr.remove(a); + a--; + NotificationCenter.getInstance().postNotificationName(NotificationCenter.messageSendError, obj.obj.getId()); + processSentMessage(obj.obj.getId()); } } - }); + if (arr.isEmpty()) { + delayedMessages.remove(location); + } + } } else if (id == NotificationCenter.FilePreparingStarted) { - MessageObject messageObject = (MessageObject)args[0]; - String finalPath = (String)args[1]; + MessageObject messageObject = (MessageObject) args[0]; + String finalPath = (String) args[1]; ArrayList arr = delayedMessages.get(messageObject.messageOwner.attachPath); if (arr != null) { for (int a = 0; a < arr.size(); a++) { DelayedMessage message = arr.get(a); if (message.obj == messageObject) { - message.videoLocation.videoEditedInfo = null; + message.videoEditedInfo = null; performSendDelayedMessage(message); arr.remove(a); - a--; break; } } @@ -209,19 +198,19 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } } } else if (id == NotificationCenter.FileNewChunkAvailable) { - MessageObject messageObject = (MessageObject)args[0]; - String finalPath = (String)args[1]; - long finalSize = (Long)args[2]; - boolean isEncrypted = ((int)messageObject.getDialogId()) == 0; + MessageObject messageObject = (MessageObject) args[0]; + String finalPath = (String) args[1]; + long finalSize = (Long) args[2]; + boolean isEncrypted = ((int) messageObject.getDialogId()) == 0; FileLoader.getInstance().checkUploadNewDataAvailable(finalPath, isEncrypted, finalSize); if (finalSize != 0) { ArrayList arr = delayedMessages.get(messageObject.messageOwner.attachPath); if (arr != null) { for (DelayedMessage message : arr) { if (message.obj == messageObject) { - message.obj.messageOwner.videoEditedInfo = null; + message.obj.videoEditedInfo = null; message.obj.messageOwner.message = "-1"; - message.obj.messageOwner.media.video.size = (int)finalSize; + message.obj.messageOwner.media.video.size = (int) finalSize; ArrayList messages = new ArrayList<>(); messages.add(message.obj.messageOwner); @@ -235,8 +224,8 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } } } else if (id == NotificationCenter.FilePreparingFailed) { - MessageObject messageObject = (MessageObject)args[0]; - String finalPath = (String)args[1]; + MessageObject messageObject = (MessageObject) args[0]; + String finalPath = (String) args[1]; stopVideoService(messageObject.messageOwner.attachPath); ArrayList arr = delayedMessages.get(finalPath); @@ -257,8 +246,8 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } } } else if (id == NotificationCenter.httpFileDidLoaded) { - String path = (String)args[0]; - String file = (String)args[1]; + String path = (String) args[0]; + String file = (String) args[1]; ArrayList arr = delayedMessages.get(path); if (arr != null) { for (final DelayedMessage message : arr) { @@ -334,7 +323,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter delayedMessages.remove(path); } } else if (id == NotificationCenter.httpFileDidFailedLoad) { - String path = (String)args[0]; + String path = (String) args[0]; ArrayList arr = delayedMessages.get(path); if (arr != null) { @@ -454,12 +443,11 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter sendMessage((TLRPC.TL_audio) messageObject.messageOwner.media.audio, messageObject.messageOwner.attachPath, did, messageObject.replyMessageObject); } else if (messageObject.messageOwner.media.video instanceof TLRPC.TL_video) { TLRPC.TL_video video = (TLRPC.TL_video) messageObject.messageOwner.media.video; - video.videoEditedInfo = messageObject.messageOwner.videoEditedInfo; - sendMessage(video, null, messageObject.messageOwner.attachPath, did, messageObject.replyMessageObject); + sendMessage(video, messageObject.videoEditedInfo, null, messageObject.messageOwner.attachPath, did, messageObject.replyMessageObject); } else if (messageObject.messageOwner.media.document instanceof TLRPC.TL_document) { sendMessage((TLRPC.TL_document) messageObject.messageOwner.media.document, null, messageObject.messageOwner.attachPath, did, messageObject.replyMessageObject); - } else if (messageObject.messageOwner.media.geo instanceof TLRPC.TL_geoPoint) { - sendMessage(messageObject.messageOwner.media.geo.lat, messageObject.messageOwner.media.geo._long, did, messageObject.replyMessageObject); + } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVenue || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeo) { + sendMessage(messageObject.messageOwner.media, did, messageObject.replyMessageObject); } else if (messageObject.messageOwner.media.phone_number != null) { TLRPC.User user = new TLRPC.TL_userContact(); user.phone = messageObject.messageOwner.media.phone_number; @@ -481,6 +469,51 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } } + public void sendSticker(TLRPC.Document document, long peer, MessageObject replyingMessageObject) { + if (document == null) { + return; + } + if (((int) peer) == 0 && document.thumb instanceof TLRPC.TL_photoSize) { + File file = FileLoader.getPathToAttach(document.thumb, true); + if (file.exists()) { + try { + int len = (int) file.length(); + byte[] arr = new byte[(int) file.length()]; + RandomAccessFile reader = new RandomAccessFile(file, "r"); + reader.readFully(arr); + TLRPC.TL_document newDocument = new TLRPC.TL_document(); + newDocument.thumb = new TLRPC.TL_photoCachedSize(); + newDocument.thumb.location = document.thumb.location; + newDocument.thumb.size = document.thumb.size; + newDocument.thumb.w = document.thumb.w; + newDocument.thumb.h = document.thumb.h; + newDocument.thumb.type = document.thumb.type; + newDocument.thumb.bytes = arr; + + newDocument.id = document.id; + newDocument.access_hash = document.access_hash; + newDocument.date = document.date; + newDocument.mime_type = document.mime_type; + newDocument.size = document.size; + newDocument.dc_id = document.dc_id; + newDocument.attributes = document.attributes; + document = newDocument; + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + } + for (int a = 0; a < document.attributes.size(); a++) { + TLRPC.DocumentAttribute attribute = document.attributes.get(a); + if (attribute instanceof TLRPC.TL_documentAttributeSticker) { + document.attributes.remove(a); + document.attributes.add(new TLRPC.TL_documentAttributeSticker_old()); + break; + } + } + SendMessagesHelper.getInstance().sendMessage((TLRPC.TL_document) document, null, null, peer, replyingMessageObject); + } + public void sendMessage(TLRPC.User user, long peer, MessageObject reply_to_msg) { sendMessage(null, null, null, null, null, null, user, null, null, null, peer, false, null, reply_to_msg, null, true); } @@ -550,6 +583,9 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter ids.add(newMsg.fwd_msg_id); newMsg.date = ConnectionsManager.getInstance().getCurrentTime(); newMsg.flags |= TLRPC.MESSAGE_FLAG_UNREAD; + if (newMsg.media instanceof TLRPC.TL_messageMediaAudio) { + newMsg.flags |= TLRPC.MESSAGE_FLAG_CONTENT_UNREAD; + } newMsg.dialog_id = peer; newMsg.to_id = to_id; MessageObject newMsgObj = new MessageObject(newMsg, null, true); @@ -560,7 +596,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter putToSendingMessages(newMsg); if (arr.size() == 100 || a == messages.size() - 1) { - MessagesStorage.getInstance().putMessages(arr, false, true, false, 0); + MessagesStorage.getInstance().putMessages(new ArrayList<>(arr), false, true, false, 0); MessagesController.getInstance().updateInterfaceWithMessages(peer, objArr); NotificationCenter.getInstance().postNotificationName(NotificationCenter.dialogsNeedReload); UserConfig.saveConfig(false); @@ -670,23 +706,23 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter sendMessage(message, null, null, null, null, null, null, null, null, null, peer, false, null, reply_to_msg, webPage, searchLinks); } - public void sendMessage(double lat, double lon, long peer, MessageObject reply_to_msg) { - sendMessage(null, lat, lon, null, null, null, null, null, null, null, peer, false, null, reply_to_msg, null, true); + public void sendMessage(TLRPC.MessageMedia location, long peer, MessageObject reply_to_msg) { + sendMessage(null, location, null, null, null, null, null, null, null, null, peer, false, null, reply_to_msg, null, true); } public void sendMessage(TLRPC.TL_photo photo, String originalPath, String path, long peer, MessageObject reply_to_msg) { - sendMessage(null, null, null, photo, null, null, null, null, null, originalPath, peer, false, path, reply_to_msg, null, true); + sendMessage(null, null, photo, null, null, null, null, null, null, originalPath, peer, false, path, reply_to_msg, null, true); } - public void sendMessage(TLRPC.TL_video video, String originalPath, String path, long peer, MessageObject reply_to_msg) { - sendMessage(null, null, null, null, video, null, null, null, null, originalPath, peer, false, path, reply_to_msg, null, true); + public void sendMessage(TLRPC.TL_video video, VideoEditedInfo videoEditedInfo, String originalPath, String path, long peer, MessageObject reply_to_msg) { + sendMessage(null, null, null, video, videoEditedInfo, null, null, null, null, originalPath, peer, false, path, reply_to_msg, null, true); } public void sendMessage(TLRPC.TL_audio audio, String path, long peer, MessageObject reply_to_msg) { sendMessage(null, null, null, null, null, null, null, null, audio, null, peer, false, path, reply_to_msg, null, true); } - private void sendMessage(String message, Double lat, Double lon, TLRPC.TL_photo photo, TLRPC.TL_video video, MessageObject msgObj, TLRPC.User user, TLRPC.TL_document document, TLRPC.TL_audio audio, String originalPath, long peer, boolean retry, String path, MessageObject reply_to_msg, TLRPC.WebPage webPage, boolean searchLinks) { + private void sendMessage(String message, TLRPC.MessageMedia location, TLRPC.TL_photo photo, TLRPC.TL_video video, VideoEditedInfo videoEditedInfo, MessageObject msgObj, TLRPC.User user, TLRPC.TL_document document, TLRPC.TL_audio audio, String originalPath, long peer, boolean retry, String path, MessageObject reply_to_msg, TLRPC.WebPage webPage, boolean searchLinks) { if (peer == 0) { return; } @@ -713,8 +749,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter type = 0; } } else if (msgObj.type == 4) { - lat = newMsg.media.geo.lat; - lon = newMsg.media.geo._long; + location = newMsg.media; type = 1; } else if (msgObj.type == 1) { if (msgObj.isForwarded()) { @@ -729,7 +764,6 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } else { type = 3; video = (TLRPC.TL_video) newMsg.media.video; - video.videoEditedInfo = newMsg.videoEditedInfo; } } else if (msgObj.type == 12) { user = new TLRPC.TL_userRequest(); @@ -760,16 +794,13 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } type = 0; newMsg.message = message; - } else if (lat != null && lon != null) { + } else if (location != null) { if (encryptedChat != null && AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) { newMsg = new TLRPC.TL_message_secret(); } else { newMsg = new TLRPC.TL_message(); } - newMsg.media = new TLRPC.TL_messageMediaGeo(); - newMsg.media.geo = new TLRPC.TL_geoPoint(); - newMsg.media.geo.lat = lat; - newMsg.media.geo._long = lon; + newMsg.media = location; newMsg.message = ""; type = 1; } else if (photo != null) { @@ -779,6 +810,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter newMsg = new TLRPC.TL_message(); } newMsg.media = new TLRPC.TL_messageMediaPhoto(); + newMsg.media.caption = photo.caption != null ? photo.caption : ""; newMsg.media.photo = photo; type = 2; newMsg.message = "-1"; @@ -795,13 +827,13 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter newMsg = new TLRPC.TL_message(); } newMsg.media = new TLRPC.TL_messageMediaVideo(); + newMsg.media.caption = video.caption != null ? video.caption : ""; newMsg.media.video = video; - newMsg.videoEditedInfo = video.videoEditedInfo; type = 3; - if (video.videoEditedInfo == null) { + if (videoEditedInfo == null) { newMsg.message = "-1"; } else { - newMsg.message = video.videoEditedInfo.getString(); + newMsg.message = videoEditedInfo.getString(); } newMsg.attachPath = path; } else if (msgObj != null) { @@ -868,6 +900,9 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } newMsg.date = ConnectionsManager.getInstance().getCurrentTime(); newMsg.flags |= TLRPC.MESSAGE_FLAG_UNREAD; + if (encryptedChat == null && high_id != 1 && newMsg.media instanceof TLRPC.TL_messageMediaAudio) { + newMsg.flags |= TLRPC.MESSAGE_FLAG_CONTENT_UNREAD; + } newMsg.dialog_id = peer; if (reply_to_msg != null) { newMsg.flags |= TLRPC.MESSAGE_FLAG_REPLY; @@ -993,13 +1028,22 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter TLRPC.InputMedia inputMedia = null; DelayedMessage delayedMessage = null; if (type == 1) { - inputMedia = new TLRPC.TL_inputMediaGeoPoint(); + if (location instanceof TLRPC.TL_messageMediaVenue) { + inputMedia = new TLRPC.TL_inputMediaVenue(); + inputMedia.address = location.address; + inputMedia.title = location.title; + inputMedia.provider = location.provider; + inputMedia.venue_id = location.venue_id; + } else { + inputMedia = new TLRPC.TL_inputMediaGeoPoint(); + } inputMedia.geo_point = new TLRPC.TL_inputGeoPoint(); - inputMedia.geo_point.lat = lat; - inputMedia.geo_point._long = lon; + inputMedia.geo_point.lat = location.geo.lat; + inputMedia.geo_point._long = location.geo._long; } else if (type == 2) { if (photo.access_hash == 0) { inputMedia = new TLRPC.TL_inputMediaUploadedPhoto(); + inputMedia.caption = photo.caption != null ? photo.caption : ""; delayedMessage = new DelayedMessage(); delayedMessage.originalPath = originalPath; delayedMessage.type = 0; @@ -1012,6 +1056,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } else { TLRPC.TL_inputMediaPhoto media = new TLRPC.TL_inputMediaPhoto(); media.id = new TLRPC.TL_inputPhoto(); + media.caption = photo.caption != null ? photo.caption : ""; media.id.id = photo.id; media.id.access_hash = photo.access_hash; inputMedia = media; @@ -1023,6 +1068,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } else { inputMedia = new TLRPC.TL_inputMediaUploadedVideo(); } + inputMedia.caption = video.caption != null ? video.caption : ""; inputMedia.duration = video.duration; inputMedia.w = video.w; inputMedia.h = video.h; @@ -1033,9 +1079,11 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter delayedMessage.obj = newMsgObj; delayedMessage.location = video.thumb.location; delayedMessage.videoLocation = video; + delayedMessage.videoEditedInfo = videoEditedInfo; } else { TLRPC.TL_inputMediaVideo media = new TLRPC.TL_inputMediaVideo(); media.id = new TLRPC.TL_inputVideo(); + media.caption = video.caption != null ? video.caption : ""; media.id.id = video.id; media.id.access_hash = video.access_hash; inputMedia = media; @@ -1088,7 +1136,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } } - TLObject reqSend = null; + TLObject reqSend; if (sendToPeers != null) { TLRPC.TL_messages_sendBroadcast request = new TLRPC.TL_messages_sendBroadcast(); @@ -1161,14 +1209,19 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter reqSend.message = ""; if (type == 1) { reqSend.media = new TLRPC.TL_decryptedMessageMediaGeoPoint(); - reqSend.media.lat = lat; - reqSend.media._long = lon; + reqSend.media.lat = location.geo.lat; + reqSend.media._long = location.geo._long; 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); reqSend.media = new TLRPC.TL_decryptedMessageMediaPhoto(); - reqSend.media.thumb = small.bytes; + ImageLoader.fillPhotoSizeWithBytes(small); + if (small.bytes != null) { + ((TLRPC.TL_decryptedMessageMediaPhoto) reqSend.media).thumb = small.bytes; + } else { + ((TLRPC.TL_decryptedMessageMediaPhoto) reqSend.media).thumb = new byte[0]; + } reqSend.media.thumb_h = small.h; reqSend.media.thumb_w = small.w; reqSend.media.w = big.w; @@ -1196,16 +1249,26 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter SecretChatHelper.getInstance().performSendEncryptedRequest(reqSend, newMsgObj.messageOwner, encryptedChat, encryptedFile, null); } } else if (type == 3) { + ImageLoader.fillPhotoSizeWithBytes(video.thumb); if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) { reqSend.media = new TLRPC.TL_decryptedMessageMediaVideo(); + if (video.thumb != null && video.thumb.bytes != null) { + ((TLRPC.TL_decryptedMessageMediaVideo) reqSend.media).thumb = video.thumb.bytes; + } else { + ((TLRPC.TL_decryptedMessageMediaVideo) reqSend.media).thumb = new byte[0]; + } } else { reqSend.media = new TLRPC.TL_decryptedMessageMediaVideo_old(); + if (video.thumb != null && video.thumb.bytes != null) { + ((TLRPC.TL_decryptedMessageMediaVideo_old) reqSend.media).thumb = video.thumb.bytes; + } else { + ((TLRPC.TL_decryptedMessageMediaVideo_old) reqSend.media).thumb = new byte[0]; + } } reqSend.media.duration = video.duration; reqSend.media.size = video.size; reqSend.media.w = video.w; reqSend.media.h = video.h; - reqSend.media.thumb = video.thumb.bytes; reqSend.media.thumb_h = video.thumb.h; reqSend.media.thumb_w = video.thumb.w; reqSend.media.mime_type = "video/mp4"; @@ -1217,6 +1280,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter delayedMessage.obj = newMsgObj; delayedMessage.encryptedChat = encryptedChat; delayedMessage.videoLocation = video; + delayedMessage.videoEditedInfo = videoEditedInfo; performSendDelayedMessage(delayedMessage); } else { TLRPC.TL_inputEncryptedFile encryptedFile = new TLRPC.TL_inputEncryptedFile(); @@ -1244,21 +1308,27 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter reqSend.media = new TLRPC.TL_decryptedMessageMediaExternalDocument(); reqSend.media.id = document.id; reqSend.media.date = document.date; + reqSend.media.access_hash = document.access_hash; reqSend.media.mime_type = document.mime_type; reqSend.media.size = document.size; - ((TLRPC.TL_decryptedMessageMediaExternalDocument) reqSend.media).thumbImage = document.thumb; reqSend.media.dc_id = document.dc_id; reqSend.media.attributes = document.attributes; + if (document.thumb == null) { + ((TLRPC.TL_decryptedMessageMediaExternalDocument) reqSend.media).thumb = new TLRPC.TL_photoSizeEmpty(); + } else { + ((TLRPC.TL_decryptedMessageMediaExternalDocument) reqSend.media).thumb = document.thumb; + } SecretChatHelper.getInstance().performSendEncryptedRequest(reqSend, newMsgObj.messageOwner, encryptedChat, null, null); } else { + ImageLoader.fillPhotoSizeWithBytes(document.thumb); reqSend.media = new TLRPC.TL_decryptedMessageMediaDocument(); reqSend.media.size = document.size; - if (!(document.thumb instanceof TLRPC.TL_photoSizeEmpty)) { - reqSend.media.thumb = document.thumb.bytes; + if (document.thumb != null && document.thumb.bytes != null) { + ((TLRPC.TL_decryptedMessageMediaDocument) reqSend.media).thumb = document.thumb.bytes; reqSend.media.thumb_h = document.thumb.h; reqSend.media.thumb_w = document.thumb.w; } else { - reqSend.media.thumb = new byte[0]; + ((TLRPC.TL_decryptedMessageMediaDocument) reqSend.media).thumb = new byte[0]; reqSend.media.thumb_h = 0; reqSend.media.thumb_w = 0; } @@ -1340,7 +1410,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } } } else if (message.type == 1) { - if (message.videoLocation.videoEditedInfo != null) { + if (message.videoEditedInfo != null) { String location = message.obj.messageOwner.attachPath; if (location == null) { location = FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE) + "/" + message.videoLocation.id + ".mp4"; @@ -1349,10 +1419,10 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter MediaController.getInstance().scheduleVideoConvert(message.obj); } else { if (message.sendRequest != null) { - TLRPC.InputMedia media = null; + TLRPC.InputMedia media; if (message.sendRequest instanceof TLRPC.TL_messages_sendMedia) { media = ((TLRPC.TL_messages_sendMedia) message.sendRequest).media; - } else if (message.sendRequest instanceof TLRPC.TL_messages_sendBroadcast) { + } else { media = ((TLRPC.TL_messages_sendBroadcast) message.sendRequest).media; } if (media.file == null) { @@ -1361,7 +1431,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter location = FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE) + "/" + message.videoLocation.id + ".mp4"; } putToDelayedMessages(location, message); - if (message.obj.messageOwner.videoEditedInfo != null) { + if (message.obj.videoEditedInfo != null) { FileLoader.getInstance().uploadFile(location, false, false, message.videoLocation.size); } else { FileLoader.getInstance().uploadFile(location, false, false); @@ -1377,7 +1447,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter location = FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE) + "/" + message.videoLocation.id + ".mp4"; } putToDelayedMessages(location, message); - if (message.obj.messageOwner.videoEditedInfo != null) { + if (message.obj.videoEditedInfo != null) { FileLoader.getInstance().uploadFile(location, true, false, message.videoLocation.size); } else { FileLoader.getInstance().uploadFile(location, true, false); @@ -1390,10 +1460,10 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter ImageLoader.getInstance().loadHttpFile(message.httpLocation, "gif"); } else { if (message.sendRequest != null) { - TLRPC.InputMedia media = null; + TLRPC.InputMedia media; if (message.sendRequest instanceof TLRPC.TL_messages_sendMedia) { media = ((TLRPC.TL_messages_sendMedia) message.sendRequest).media; - } else if (message.sendRequest instanceof TLRPC.TL_messages_sendBroadcast) { + } else { media = ((TLRPC.TL_messages_sendBroadcast) message.sendRequest).media; } if (media.file == null) { @@ -1514,7 +1584,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter ArrayList arr = new ArrayList<>(); MessageObject messageObject = new MessageObject(message, null, false); arr.add(messageObject); - MessagesController.getInstance().updateInterfaceWithMessages(messageObject.getDialogId(), arr, isBroadcast); + MessagesController.getInstance().updateInterfaceWithMessages(messageObject.getDialogId(), arr, true); } NotificationCenter.getInstance().postNotificationName(NotificationCenter.dialogsNeedReload); } @@ -1571,25 +1641,28 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter MessagesStorage.getInstance().putSentFile(originalPath, sentMessage.media.photo, 0); for (TLRPC.PhotoSize size : sentMessage.media.photo.sizes) { - if (size instanceof TLRPC.TL_photoSizeEmpty) { + if (size == null || size instanceof TLRPC.TL_photoSizeEmpty || size.type == null) { continue; } for (TLRPC.PhotoSize size2 : newMsg.media.photo.sizes) { - if (size2.location != null && size2.location.volume_id == Integer.MIN_VALUE && size.type.equals(size2.type) || size.w == size2.w && size.h == size2.h) { + if (size2 == null || size2.location == null || size2.type == null) { + continue; + } + if (size2.location.volume_id == Integer.MIN_VALUE && size.type.equals(size2.type) || size.w == size2.w && size.h == size2.h) { String fileName = size2.location.volume_id + "_" + size2.location.local_id; String fileName2 = size.location.volume_id + "_" + size.location.local_id; if (fileName.equals(fileName2)) { break; } File cacheFile = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName + ".jpg"); - File cacheFile2 = null; + File cacheFile2; if (sentMessage.media.photo.sizes.size() == 1 || size.w > 90 || size.h > 90) { cacheFile2 = FileLoader.getPathToAttach(size); } else { cacheFile2 = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName2 + ".jpg"); } cacheFile.renameTo(cacheFile2); - ImageLoader.getInstance().replaceImageInCache(fileName, fileName2); + ImageLoader.getInstance().replaceImageInCache(fileName, fileName2, size.location); size2.location = size.location; break; } @@ -1611,7 +1684,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter File cacheFile = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName + ".jpg"); File cacheFile2 = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName2 + ".jpg"); cacheFile.renameTo(cacheFile2); - ImageLoader.getInstance().replaceImageInCache(fileName, fileName2); + ImageLoader.getInstance().replaceImageInCache(fileName, fileName2, size.location); size2.location = size.location; } } @@ -1642,7 +1715,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter File cacheFile = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName + ".jpg"); File cacheFile2 = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName2 + ".jpg"); cacheFile.renameTo(cacheFile2); - ImageLoader.getInstance().replaceImageInCache(fileName, fileName2); + ImageLoader.getInstance().replaceImageInCache(fileName, fileName2, size.location); size2.location = size.location; } } else if (MessageObject.isStickerMessage(sentMessage) && size2.location != null) { @@ -1697,6 +1770,10 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter arrayList.add(message); } + protected ArrayList getDelayedMessages(String location) { + return delayedMessages.get(location); + } + protected long getNextRandomId() { long val = 0; while (val == 0) { @@ -1749,7 +1826,6 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter photo.user_id = UserConfig.getClientUserId(); photo.date = ConnectionsManager.getInstance().getCurrentTime(); photo.sizes = sizes; - photo.caption = ""; photo.geo = new TLRPC.TL_geoPointEmpty(); return photo; } @@ -1778,13 +1854,10 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter return false; } - boolean isEncrypted = (int)dialog_id == 0; + boolean isEncrypted = (int) dialog_id == 0; boolean allowSticker = !isEncrypted; String name = f.getName(); - if (name == null) { - name = "noname"; - } String ext = ""; int idx = path.lastIndexOf("."); if (idx != -1) { @@ -1808,14 +1881,18 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter TLRPC.TL_documentAttributeFilename fileName = new TLRPC.TL_documentAttributeFilename(); fileName.file_name = name; document.attributes.add(fileName); - document.size = (int)f.length(); + document.size = (int) f.length(); document.dc_id = 0; if (ext.length() != 0) { - String mimeType = myMime.getMimeTypeFromExtension(ext.toLowerCase()); - if (mimeType != null) { - document.mime_type = mimeType; + if (ext.toLowerCase().equals("webp")) { + document.mime_type = "image/webp"; } else { - document.mime_type = "application/octet-stream"; + String mimeType = myMime.getMimeTypeFromExtension(ext.toLowerCase()); + if (mimeType != null) { + document.mime_type = mimeType; + } else { + document.mime_type = "application/octet-stream"; + } } } else { document.mime_type = "application/octet-stream"; @@ -1843,12 +1920,13 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter FileLog.e("tmessages", e); } if (bmOptions.outWidth != 0 && bmOptions.outHeight != 0 && bmOptions.outWidth <= 800 && bmOptions.outHeight <= 800) { - TLRPC.TL_documentAttributeSticker attributeSticker = null; + TLRPC.TL_documentAttributeSticker attributeSticker; if (isEncrypted) { attributeSticker = new TLRPC.TL_documentAttributeSticker_old(); } else { attributeSticker = new TLRPC.TL_documentAttributeSticker(); attributeSticker.alt = ""; + attributeSticker.stickerset = new TLRPC.TL_inputStickerSetEmpty(); } document.attributes.add(attributeSticker); TLRPC.TL_documentAttributeImageSize attributeImageSize = new TLRPC.TL_documentAttributeImageSize(); @@ -1929,9 +2007,10 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter }).start(); } - public static void prepareSendingPhoto(String imageFilePath, Uri imageUri, long dialog_id, MessageObject reply_to_msg) { + public static void prepareSendingPhoto(String imageFilePath, Uri imageUri, long dialog_id, MessageObject reply_to_msg, CharSequence caption) { ArrayList paths = null; ArrayList uris = null; + ArrayList captions = null; if (imageFilePath != null && imageFilePath.length() != 0) { paths = new ArrayList<>(); paths.add(imageFilePath); @@ -1940,7 +2019,11 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter uris = new ArrayList<>(); uris.add(imageUri); } - prepareSendingPhotos(paths, uris, dialog_id, reply_to_msg); + if (caption != null) { + captions = new ArrayList<>(); + captions.add(caption.toString()); + } + prepareSendingPhotos(paths, uris, dialog_id, reply_to_msg, captions); } public static void prepareSendingPhotosSearch(final ArrayList photos, final long dialog_id, final MessageObject reply_to_msg) { @@ -1950,8 +2033,9 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter new Thread(new Runnable() { @Override public void run() { - boolean isEncrypted = (int)dialog_id == 0; - for (final MediaController.SearchImage searchImage : photos) { + boolean isEncrypted = (int) dialog_id == 0; + for (int a = 0; a < photos.size(); a++) { + final MediaController.SearchImage searchImage = photos.get(a); if (searchImage.type == 1) { TLRPC.TL_document document = null; if (!isEncrypted) { @@ -1960,7 +2044,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter String md5 = Utilities.MD5(searchImage.imageUrl) + "." + ImageLoader.getHttpUrlExtension(searchImage.imageUrl); File cacheFile = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), md5); if (document == null) { - File thumbFile = null; + File thumbFile; document = new TLRPC.TL_document(); document.id = 0; document.date = ConnectionsManager.getInstance().getCurrentTime(); @@ -2017,19 +2101,22 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter if (photo == null) { String md5 = Utilities.MD5(searchImage.imageUrl) + "." + ImageLoader.getHttpUrlExtension(searchImage.imageUrl); File cacheFile = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), md5); - if (cacheFile.exists()) { + if (cacheFile.exists() && cacheFile.length() != 0) { photo = SendMessagesHelper.getInstance().generatePhotoSizes(cacheFile.toString(), null); - needDownloadHttp = false; - } else { + if (photo != null) { + needDownloadHttp = false; + } + } + if (photo == null) { md5 = Utilities.MD5(searchImage.thumbUrl) + "." + ImageLoader.getHttpUrlExtension(searchImage.thumbUrl); cacheFile = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), md5); if (cacheFile.exists()) { photo = SendMessagesHelper.getInstance().generatePhotoSizes(cacheFile.toString(), null); - } else { + } + if (photo == null) { photo = new TLRPC.TL_photo(); photo.user_id = UserConfig.getClientUserId(); photo.date = ConnectionsManager.getInstance().getCurrentTime(); - photo.caption = ""; photo.geo = new TLRPC.TL_geoPointEmpty(); TLRPC.TL_photoSize photoSize = new TLRPC.TL_photoSize(); photoSize.w = searchImage.width; @@ -2042,6 +2129,9 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } } if (photo != null) { + if (searchImage.caption != null) { + photo.caption = searchImage.caption.toString(); + } final String originalPathFinal = searchImage.imageUrl; final TLRPC.TL_photo photoFinal = photo; final boolean needDownloadHttpFinal = needDownloadHttp; @@ -2058,7 +2148,32 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter }).start(); } - public static void prepareSendingPhotos(ArrayList paths, ArrayList uris, final long dialog_id, final MessageObject reply_to_msg) { + private static String getTrimmedString(String src) { + String result = src.trim(); + if (result.length() == 0) { + return result; + } + while (src.startsWith("\n")) { + src = src.substring(1); + } + while (src.endsWith("\n")) { + src = src.substring(0, src.length() - 1); + } + return src; + } + + public static void prepareSendingText(String text, long dialog_id) { + text = getTrimmedString(text); + if (text.length() != 0) { + int count = (int) Math.ceil(text.length() / 4096.0f); + for (int a = 0; a < count; a++) { + String mess = text.substring(a * 4096, Math.min((a + 1) * 4096, text.length())); + SendMessagesHelper.getInstance().sendMessage(mess, dialog_id, null, null, true); + } + } + } + + public static void prepareSendingPhotos(ArrayList paths, ArrayList uris, final long dialog_id, final MessageObject reply_to_msg, final ArrayList captions) { if (paths == null && uris == null || paths != null && paths.isEmpty() || uris != null && uris.isEmpty()) { return; } @@ -2073,13 +2188,14 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter new Thread(new Runnable() { @Override public void run() { - boolean isEncrypted = (int)dialog_id == 0; + boolean isEncrypted = (int) dialog_id == 0; ArrayList sendAsDocuments = null; ArrayList sendAsDocumentsOriginal = null; int count = !pathsCopy.isEmpty() ? pathsCopy.size() : urisCopy.size(); String path = null; Uri uri = null; + String extension = null; for (int a = 0; a < count; a++) { if (!pathsCopy.isEmpty()) { path = pathsCopy.get(a); @@ -2090,22 +2206,29 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter String originalPath = path; String tempPath = path; if (tempPath == null && uri != null) { - tempPath = Utilities.getPath(uri); + tempPath = AndroidUtilities.getPath(uri); originalPath = uri.toString(); } boolean isDocument = false; if (tempPath != null && (tempPath.endsWith(".gif") || tempPath.endsWith(".webp"))) { + if (tempPath.endsWith(".gif")) { + extension = "gif"; + } else { + extension = "webp"; + } isDocument = true; } else if (tempPath == null && uri != null) { if (MediaController.isGif(uri)) { isDocument = true; originalPath = uri.toString(); tempPath = MediaController.copyDocumentToCache(uri, "gif"); + extension = "gif"; } else if (MediaController.isWebp(uri)) { isDocument = true; originalPath = uri.toString(); tempPath = MediaController.copyDocumentToCache(uri, "webp"); + extension = "webp"; } } @@ -2127,13 +2250,16 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter if (!isEncrypted) { photo = (TLRPC.TL_photo) MessagesStorage.getInstance().getSentFile(originalPath, !isEncrypted ? 0 : 3); if (photo == null && uri != null) { - photo = (TLRPC.TL_photo) MessagesStorage.getInstance().getSentFile(Utilities.getPath(uri), !isEncrypted ? 0 : 3); + photo = (TLRPC.TL_photo) MessagesStorage.getInstance().getSentFile(AndroidUtilities.getPath(uri), !isEncrypted ? 0 : 3); } } if (photo == null) { photo = SendMessagesHelper.getInstance().generatePhotoSizes(path, uri); } if (photo != null) { + if (captions != null) { + photo.caption = captions.get(a); + } final String originalPathFinal = originalPath; final TLRPC.TL_photo photoFinal = photo; AndroidUtilities.runOnUIThread(new Runnable() { @@ -2147,14 +2273,14 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } if (sendAsDocuments != null && !sendAsDocuments.isEmpty()) { for (int a = 0; a < sendAsDocuments.size(); a++) { - prepareSendingDocumentInternal(sendAsDocuments.get(a), sendAsDocumentsOriginal.get(a), null, "gif", dialog_id, reply_to_msg); + prepareSendingDocumentInternal(sendAsDocuments.get(a), sendAsDocumentsOriginal.get(a), null, extension, dialog_id, reply_to_msg); } } } }).start(); } - public static void prepareSendingVideo(final String videoPath, final long estimatedSize, final long duration, final int width, final int height, final TLRPC.VideoEditedInfo videoEditedInfo, final long dialog_id, final MessageObject reply_to_msg) { + public static void prepareSendingVideo(final String videoPath, final long estimatedSize, final long duration, final int width, final int height, final VideoEditedInfo videoEditedInfo, final long dialog_id, final MessageObject reply_to_msg) { if (videoPath == null || videoPath.length() == 0) { return; } @@ -2162,7 +2288,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter @Override public void run() { - boolean isEncrypted = (int)dialog_id == 0; + boolean isEncrypted = (int) dialog_id == 0; if (videoEditedInfo != null || videoPath.endsWith("mp4")) { String path = videoPath; @@ -2190,7 +2316,6 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } else { video.thumb.type = "s"; } - video.caption = ""; video.mime_type = "video/mp4"; video.id = 0; UserConfig.saveConfig(false); @@ -2205,14 +2330,13 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter video.h = height; } video.size = (int) estimatedSize; - video.videoEditedInfo = videoEditedInfo; String fileName = Integer.MIN_VALUE + "_" + UserConfig.lastLocalId + ".mp4"; UserConfig.lastLocalId--; File cacheFile = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName); UserConfig.saveConfig(false); path = cacheFile.getAbsolutePath(); } else { - if (temp != null && temp.exists()) { + if (temp.exists()) { video.size = (int) temp.length(); } boolean infoObtained = false; @@ -2240,7 +2364,6 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter try { if (mediaMetadataRetriever != null) { mediaMetadataRetriever.release(); - mediaMetadataRetriever = null; } } catch (Exception e) { FileLog.e("tmessages", e); @@ -2268,7 +2391,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { - SendMessagesHelper.getInstance().sendMessage(videoFinal, originalPathFinal, finalPath, dialog_id, reply_to_msg); + SendMessagesHelper.getInstance().sendMessage(videoFinal, videoEditedInfo, originalPathFinal, finalPath, dialog_id, reply_to_msg); } }); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/android/SmsListener.java b/TMessagesProj/src/main/java/org/telegram/android/SmsListener.java index 78e179605..0c1df4802 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/SmsListener.java +++ b/TMessagesProj/src/main/java/org/telegram/android/SmsListener.java @@ -44,11 +44,16 @@ public class SmsListener extends BroadcastReceiver { try { Pattern pattern = Pattern.compile("[0-9]+"); - Matcher matcher = pattern.matcher(wholeString); + final Matcher matcher = pattern.matcher(wholeString); if (matcher.find()) { String str = matcher.group(0); if (str.length() >= 3) { - NotificationCenter.getInstance().postNotificationName(NotificationCenter.didReceiveSmsCode, matcher.group(0)); + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + NotificationCenter.getInstance().postNotificationName(NotificationCenter.didReceiveSmsCode, matcher.group(0)); + } + }); } } } catch (Throwable e) { diff --git a/TMessagesProj/src/main/java/org/telegram/android/VideoEditedInfo.java b/TMessagesProj/src/main/java/org/telegram/android/VideoEditedInfo.java new file mode 100644 index 000000000..ef14750e3 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/VideoEditedInfo.java @@ -0,0 +1,51 @@ +/* + * This is the source code of Telegram for Android v. 2.x.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2014. + */ + +package org.telegram.android; + +import java.util.Locale; + +public class VideoEditedInfo { + public long startTime; + public long endTime; + public int rotationValue; + public int originalWidth; + public int originalHeight; + public int resultWidth; + public int resultHeight; + public int bitrate; + public String originalPath; + + public String getString() { + return String.format(Locale.US, "-1_%d_%d_%d_%d_%d_%d_%d_%d_%s", startTime, endTime, rotationValue, originalWidth, originalHeight, bitrate, resultWidth, resultHeight, originalPath); + } + + public void parseString(String string) { + if (string.length() < 6) { + return; + } + String args[] = string.split("_"); + if (args.length >= 10) { + startTime = Long.parseLong(args[1]); + endTime = Long.parseLong(args[2]); + rotationValue = Integer.parseInt(args[3]); + originalWidth = Integer.parseInt(args[4]); + originalHeight = Integer.parseInt(args[5]); + bitrate = Integer.parseInt(args[6]); + resultWidth = Integer.parseInt(args[7]); + resultHeight = Integer.parseInt(args[8]); + for (int a = 9; a < args.length; a++) { + if (originalPath == null) { + originalPath = args[a]; + } else { + originalPath += "_" + args[a]; + } + } + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/query/ReplyMessageQuery.java b/TMessagesProj/src/main/java/org/telegram/android/query/ReplyMessageQuery.java index da9717dd1..bb01bdd5a 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/query/ReplyMessageQuery.java +++ b/TMessagesProj/src/main/java/org/telegram/android/query/ReplyMessageQuery.java @@ -21,7 +21,6 @@ import org.telegram.messenger.ByteBufferDesc; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.FileLog; import org.telegram.messenger.RPCRequest; -import org.telegram.messenger.TLClassStore; import org.telegram.messenger.TLObject; import org.telegram.messenger.TLRPC; @@ -65,7 +64,7 @@ public class ReplyMessageQuery { while (cursor.next()) { ByteBufferDesc data = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - TLRPC.Message message = (TLRPC.Message) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); message.id = cursor.intValue(1); message.date = cursor.intValue(2); message.dialog_id = dialog_id; diff --git a/TMessagesProj/src/main/java/org/telegram/android/query/SharedMediaQuery.java b/TMessagesProj/src/main/java/org/telegram/android/query/SharedMediaQuery.java index aab9255e6..345b0e49d 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/query/SharedMediaQuery.java +++ b/TMessagesProj/src/main/java/org/telegram/android/query/SharedMediaQuery.java @@ -20,7 +20,6 @@ import org.telegram.messenger.ByteBufferDesc; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.FileLog; import org.telegram.messenger.RPCRequest; -import org.telegram.messenger.TLClassStore; import org.telegram.messenger.TLObject; import org.telegram.messenger.TLRPC; @@ -178,7 +177,7 @@ public class SharedMediaQuery { } final ArrayList objects = new ArrayList<>(); for (TLRPC.Message message : res.messages) { - objects.add(new MessageObject(message, usersLocal, false)); + objects.add(new MessageObject(message, usersLocal, true)); } AndroidUtilities.runOnUIThread(new Runnable() { @@ -324,7 +323,7 @@ public class SharedMediaQuery { while (cursor.next()) { ByteBufferDesc data = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - TLRPC.Message message = (TLRPC.Message) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); message.id = cursor.intValue(1); message.dialog_id = uid; if ((int)uid == 0) { diff --git a/TMessagesProj/src/main/java/org/telegram/android/query/StickersQuery.java b/TMessagesProj/src/main/java/org/telegram/android/query/StickersQuery.java new file mode 100644 index 000000000..9b247d8e8 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/query/StickersQuery.java @@ -0,0 +1,437 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.android.query; + +import android.app.AlertDialog; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Message; +import android.widget.Toast; + +import org.telegram.SQLite.SQLiteCursor; +import org.telegram.SQLite.SQLitePreparedStatement; +import org.telegram.android.AndroidUtilities; +import org.telegram.android.LocaleController; +import org.telegram.android.MessagesStorage; +import org.telegram.android.NotificationCenter; +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.TLObject; +import org.telegram.messenger.TLRPC; +import org.telegram.messenger.Utilities; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.StickersAlert; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; + +public class StickersQuery { + + private static String hash; + private static int loadDate; + private static ArrayList stickers = new ArrayList<>(); + private static HashMap> allStickers = new HashMap<>(); + private static ArrayList stickerPacks = new ArrayList<>(); + private static ArrayList stickerSets = new ArrayList<>(); + private static HashMap> stickersBySets = new HashMap<>(); + private static HashMap stickersByEmoji = new HashMap<>(); + private static boolean loadingStickers; + private static boolean stickersLoaded; + private static boolean hideMainStickersPack; + + public static void checkStickers() { + if (!loadingStickers && (!stickersLoaded || loadDate < (System.currentTimeMillis() / 1000 - 60 * 60))) { + loadStickers(true, false); + } + } + + public static boolean isLoadingStickers() { + return loadingStickers; + } + + public static HashMap> getAllStickers() { + return allStickers; + } + + public static ArrayList getStickersForSet(long id) { + return stickersBySets.get(id); + } + + public static ArrayList getStickerPacks() { + return stickerPacks; + } + + public static ArrayList getStickers() { + return stickers; + } + + public static ArrayList getStickerSets() { + return stickerSets; + } + + public static boolean isStickerPackInstalled(long id) { + return stickersBySets.containsKey(id); + } + + public static String getEmojiForSticker(long id) { + String value = stickersByEmoji.get(id); + return value != null ? value : ""; + } + + public static void loadStickers(boolean cache, boolean force) { + if (loadingStickers) { + return; + } + loadingStickers = true; + if (cache) { + MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() { + @Override + public void run() { + TLRPC.messages_AllStickers result = null; + int date = 0; + try { + SQLiteCursor cursor = MessagesStorage.getInstance().getDatabase().queryFinalized("SELECT value FROM keyvalue WHERE id = 'hide_stickers'"); + if (cursor.next()) { + int value = Utilities.parseInt(cursor.stringValue(0)); + hideMainStickersPack = value == 1; + } + cursor.dispose(); + + cursor = MessagesStorage.getInstance().getDatabase().queryFinalized("SELECT data, date FROM stickers WHERE 1"); + ArrayList loadedUsers = new ArrayList<>(); + if (cursor.next()) { + ByteBufferDesc data = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(cursor.byteArrayLength(0)); + if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { + result = TLRPC.messages_AllStickers.TLdeserialize(data, data.readInt32(false), false); + } + date = cursor.intValue(1); + MessagesStorage.getInstance().getBuffersStorage().reuseFreeBuffer(data); + } + cursor.dispose(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + processLoadedStickers(result, true, date); + } + }); + } else { + TLRPC.TL_messages_getAllStickers req = new TLRPC.TL_messages_getAllStickers(); + req.hash = hash == null || force ? "" : hash; + 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() { + processLoadedStickers((TLRPC.messages_AllStickers) response, false, (int) (System.currentTimeMillis() / 1000)); + } + }); + } + }); + } + } + + private static void putStickersToCache(final TLRPC.TL_messages_allStickers stickers) { + MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() { + @Override + public void run() { + try { + SQLitePreparedStatement state = MessagesStorage.getInstance().getDatabase().executeFast("REPLACE INTO stickers VALUES(?, ?, ?)"); + state.requery(); + ByteBufferDesc data = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(stickers.getObjectSize()); + stickers.serializeToStream(data); + state.bindInteger(1, 1); + state.bindByteBuffer(2, data.buffer); + state.bindInteger(3, (int) (System.currentTimeMillis() / 1000)); + state.step(); + MessagesStorage.getInstance().getBuffersStorage().reuseFreeBuffer(data); + state.dispose(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + }); + } + + private static long getStickerSetId(TLRPC.Document document) { + for (TLRPC.DocumentAttribute attribute : document.attributes) { + if (attribute instanceof TLRPC.TL_documentAttributeSticker) { + if (attribute.stickerset instanceof TLRPC.TL_inputStickerSetID) { + return attribute.stickerset.id; + } + break; + } + } + return -1; + } + + private static void processLoadedStickers(final TLRPC.messages_AllStickers res, final boolean cache, final int date) { + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + loadingStickers = false; + stickersLoaded = true; + } + }); + Utilities.stageQueue.postRunnable(new Runnable() { + @Override + public void run() { + if ((res == null || date < (int) (System.currentTimeMillis() / 1000 - 60 * 60)) && cache) { + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + loadStickers(false, false); + } + }); + if (res == null) { + return; + } + } + if (res instanceof TLRPC.TL_messages_allStickers) { + HashMap documents = new HashMap<>(); + final HashMap> sets = new HashMap<>(); + final ArrayList allDocuments = new ArrayList<>(); + final HashMap stickersEmoji = new HashMap<>(); + for (TLRPC.Document document : res.documents) { + if (document == null) { + continue; + } + + documents.put(document.id, document); + long setId = getStickerSetId(document); + if (setId != -1 || setId == -1 && !hideMainStickersPack) { + allDocuments.add(document); + } + ArrayList docs = sets.get(setId); + if (docs == null) { + docs = new ArrayList<>(); + sets.put(setId, docs); + if (setId == -1) { + boolean contain = false; + for (TLRPC.TL_stickerSet set : res.sets) { + if (set.id == setId) { + contain = true; + break; + } + } + if (!contain) { + TLRPC.TL_stickerSet set = new TLRPC.TL_stickerSet(); + set.title = set.short_name = ""; + set.id = -1; + res.sets.add(0, set); + } + } + } + docs.add(document); + } + final HashMap> result = new HashMap<>(); + for (TLRPC.TL_stickerPack stickerPack : res.packs) { + if (stickerPack != null && stickerPack.emoticon != null) { + stickerPack.emoticon = stickerPack.emoticon.replace("\uFE0F", ""); + ArrayList arrayList = result.get(stickerPack.emoticon); + for (Long id : stickerPack.documents) { + if (!stickersEmoji.containsKey(id)) { + stickersEmoji.put(id, stickerPack.emoticon); + } + TLRPC.Document document = documents.get(id); + if (document != null) { + long setId = getStickerSetId(document); + if (setId == -1 && hideMainStickersPack) { + continue; + } + + if (arrayList == null) { + arrayList = new ArrayList<>(); + result.put(stickerPack.emoticon, arrayList); + } + arrayList.add(document); + } + } + } + } + Collections.sort(allDocuments, new Comparator() { + @Override + public int compare(TLRPC.Document lhs, TLRPC.Document rhs) { + long lid = getStickerSetId(lhs); + long rid = getStickerSetId(rhs); + if (lid < rid) { + return -1; + } else if (lid > rid) { + return 1; + } + return 0; + } + }); + if (!cache) { + putStickersToCache((TLRPC.TL_messages_allStickers) res); + } + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + stickerSets = res.sets; + allStickers = result; + stickers = allDocuments; + stickersBySets = sets; + stickersByEmoji = stickersEmoji; + hash = res.hash; + loadDate = date; + NotificationCenter.getInstance().postNotificationName(NotificationCenter.stickersDidLoaded); + } + }); + } + } + }); + } + + public static void loadStickers(final BaseFragment fragment, final TLRPC.InputStickerSet stickerSet) { + if (fragment == null || stickerSet == null) { + return; + } + + final ProgressDialog progressDialog = new ProgressDialog(fragment.getParentActivity()); + progressDialog.setMessage(LocaleController.getString("Loading", R.string.Loading)); + progressDialog.setCanceledOnTouchOutside(false); + progressDialog.setCancelable(false); + + TLRPC.TL_messages_getStickerSet req = new TLRPC.TL_messages_getStickerSet(); + req.stickerset = stickerSet; + + final long 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() { + try { + progressDialog.dismiss(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + if (fragment != null && fragment.getParentActivity() != null && !fragment.getParentActivity().isFinishing()) { + if (error == null) { + final TLRPC.TL_messages_stickerSet res = (TLRPC.TL_messages_stickerSet) response; + + StickersAlert alert = new StickersAlert(fragment.getParentActivity(), res.set, res.documents); + if (res.set == null || !StickersQuery.isStickerPackInstalled(res.set.id)) { + alert.setButton(AlertDialog.BUTTON_POSITIVE, LocaleController.getString("AddStickers", R.string.AddStickers), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + TLRPC.TL_messages_installStickerSet req = new TLRPC.TL_messages_installStickerSet(); + req.stickerset = stickerSet; + ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() { + @Override + public void run(TLObject response, final TLRPC.TL_error error) { + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + if (fragment != null && fragment.getParentActivity() != null) { + if (error == null) { + Toast.makeText(fragment.getParentActivity(), LocaleController.getString("AddStickersInstalled", R.string.AddStickersInstalled), Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(fragment.getParentActivity(), LocaleController.getString("ErrorOccurred", R.string.ErrorOccurred), Toast.LENGTH_SHORT).show(); + } + } + loadStickers(false, true); + } + }); + } + }); + } + }); + } else { + alert.setButton(AlertDialog.BUTTON_NEUTRAL, LocaleController.getString("StickersRemove", R.string.StickersRemove), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + removeStickersSet(fragment.getParentActivity(), res.set); + } + }); + } + alert.setButton(AlertDialog.BUTTON_NEGATIVE, LocaleController.getString("Close", R.string.Close), (Message) null); + fragment.setVisibleDialog(alert); + alert.show(); + } else { + Toast.makeText(fragment.getParentActivity(), LocaleController.getString("AddStickersNotFound", R.string.AddStickersNotFound), Toast.LENGTH_SHORT).show(); + } + } + } + }); + } + }); + + progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, LocaleController.getString("Cancel", R.string.Cancel), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + ConnectionsManager.getInstance().cancelRpc(reqId, true); + try { + dialog.dismiss(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + }); + fragment.setVisibleDialog(progressDialog); + progressDialog.show(); + } + + public static void setHideMainStickersPack(final boolean value) { + hideMainStickersPack = value; + MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() { + @Override + public void run() { + try { + SQLitePreparedStatement state = MessagesStorage.getInstance().getDatabase().executeFast("REPLACE INTO keyvalue VALUES(?, ?)"); + state.requery(); + state.bindString(1, "hide_stickers"); + state.bindString(2, value ? "1" : "0"); + state.step(); + state.dispose(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + }); + } + + public static void removeStickersSet(final Context context, TLRPC.TL_stickerSet stickerSet) { + TLRPC.TL_messages_uninstallStickerSet req = new TLRPC.TL_messages_uninstallStickerSet(); + req.stickerset = new TLRPC.TL_inputStickerSetID(); + req.stickerset.access_hash = stickerSet.access_hash; + req.stickerset.id = stickerSet.id; + ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() { + @Override + public void run(TLObject response, final TLRPC.TL_error error) { + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + try { + if (error == null) { + Toast.makeText(context, LocaleController.getString("StickersRemoved", R.string.StickersRemoved), Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(context, LocaleController.getString("ErrorOccurred", R.string.ErrorOccurred), Toast.LENGTH_SHORT).show(); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + loadStickers(false, true); + } + }); + } + }); + } + + public static boolean getHideMainStickersPack() { + return hideMainStickersPack; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/util/SortedList.java b/TMessagesProj/src/main/java/org/telegram/android/support/util/SortedList.java new file mode 100644 index 000000000..688e032c8 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/util/SortedList.java @@ -0,0 +1,633 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.support.v7.util; + +import java.lang.reflect.Array; + +/** + * A Sorted list implementation that can keep items in order and also notify for changes in the + * list + * such that it can be bound to a {@link android.support.v7.widget.RecyclerView.Adapter + * RecyclerView.Adapter}. + *

+ * It keeps items ordered using the {@link Callback#compare(Object, Object)} method and uses + * binary search to retrieve items. If the sorting criteria of your items may change, make sure you + * call appropriate methods while editing them to avoid data inconsistencies. + *

+ * You can control the order of items and change notifications via the {@link Callback} parameter. + */ +@SuppressWarnings("unchecked") +public class SortedList { + + /** + * Used by {@link #indexOf(Object)} when he item cannot be found in the list. + */ + public static final int INVALID_POSITION = -1; + + private static final int MIN_CAPACITY = 10; + private static final int CAPACITY_GROWTH = MIN_CAPACITY; + private static final int INSERTION = 1; + private static final int DELETION = 1 << 1; + private static final int LOOKUP = 1 << 2; + T[] mData; + + /** + * The callback instance that controls the behavior of the SortedList and get notified when + * changes happen. + */ + private Callback mCallback; + + private BatchedCallback mBatchedCallback; + + private int mSize; + private final Class mTClass; + + /** + * Creates a new SortedList of type T. + * + * @param klass The class of the contents of the SortedList. + * @param callback The callback that controls the behavior of SortedList. + */ + public SortedList(Class klass, Callback callback) { + this(klass, callback, MIN_CAPACITY); + } + + /** + * Creates a new SortedList of type T. + * + * @param klass The class of the contents of the SortedList. + * @param callback The callback that controls the behavior of SortedList. + * @param initialCapacity The initial capacity to hold items. + */ + public SortedList(Class klass, Callback callback, int initialCapacity) { + mTClass = klass; + mData = (T[]) Array.newInstance(klass, initialCapacity); + mCallback = callback; + mSize = 0; + } + + /** + * The number of items in the list. + * + * @return The number of items in the list. + */ + public int size() { + return mSize; + } + + /** + * Adds the given item to the list. If this is a new item, SortedList calls + * {@link Callback#onInserted(int, int)}. + *

+ * If the item already exists in the list and its sorting criteria is not changed, it is + * replaced with the existing Item. SortedList uses + * {@link Callback#areItemsTheSame(Object, Object)} to check if two items are the same item + * and uses {@link Callback#areContentsTheSame(Object, Object)} to decide whether it should + * call {@link Callback#onChanged(int, int)} or not. In both cases, it always removes the + * reference to the old item and puts the new item into the backing array even if + * {@link Callback#areContentsTheSame(Object, Object)} returns false. + *

+ * If the sorting criteria of the item is changed, SortedList won't be able to find + * its duplicate in the list which will result in having a duplicate of the Item in the list. + * If you need to update sorting criteria of an item that already exists in the list, + * use {@link #updateItemAt(int, Object)}. You can find the index of the item using + * {@link #indexOf(Object)} before you update the object. + * + * @param item The item to be added into the list. + * @return The index of the newly added item. + * @see {@link Callback#compare(Object, Object)} + * @see {@link Callback#areItemsTheSame(Object, Object)} + * @see {@link Callback#areContentsTheSame(Object, Object)}} + */ + public int add(T item) { + return add(item, true); + } + + /** + * Batches adapter updates that happen between calling this method until calling + * {@link #endBatchedUpdates()}. For example, if you add multiple items in a loop + * and they are placed into consecutive indices, SortedList calls + * {@link Callback#onInserted(int, int)} only once with the proper item count. If an event + * cannot be merged with the previous event, the previous event is dispatched + * to the callback instantly. + *

+ * After running your data updates, you must call {@link #endBatchedUpdates()} + * which will dispatch any deferred data change event to the current callback. + *

+ * A sample implementation may look like this: + *

+     *     mSortedList.beginBatchedUpdates();
+     *     try {
+     *         mSortedList.add(item1)
+     *         mSortedList.add(item2)
+     *         mSortedList.remove(item3)
+     *         ...
+     *     } finally {
+     *         mSortedList.endBatchedUpdates();
+     *     }
+     * 
+ *

+ * Instead of using this method to batch calls, you can use a Callback that extends + * {@link BatchedCallback}. In that case, you must make sure that you are manually calling + * {@link BatchedCallback#dispatchLastEvent()} right after you complete your data changes. + * Failing to do so may create data inconsistencies with the Callback. + *

+ * If the current Callback in an instance of {@link BatchedCallback}, calling this method + * has no effect. + */ + public void beginBatchedUpdates() { + if (mCallback instanceof BatchedCallback) { + return; + } + if (mBatchedCallback == null) { + mBatchedCallback = new BatchedCallback(mCallback); + } + mCallback = mBatchedCallback; + } + + /** + * Ends the update transaction and dispatches any remaining event to the callback. + */ + public void endBatchedUpdates() { + if (mCallback instanceof BatchedCallback) { + ((BatchedCallback) mCallback).dispatchLastEvent(); + } + if (mCallback == mBatchedCallback) { + mCallback = mBatchedCallback.mWrappedCallback; + } + } + + private int add(T item, boolean notify) { + int index = findIndexOf(item, INSERTION); + if (index == INVALID_POSITION) { + index = 0; + } else if (index < mSize) { + T existing = mData[index]; + if (mCallback.areItemsTheSame(existing, item)) { + if (mCallback.areContentsTheSame(existing, item)) { + //no change but still replace the item + mData[index] = item; + return index; + } else { + mData[index] = item; + mCallback.onChanged(index, 1); + return index; + } + } + } + addToData(index, item); + if (notify) { + mCallback.onInserted(index, 1); + } + return index; + } + + /** + * Removes the provided item from the list and calls {@link Callback#onRemoved(int, int)}. + * + * @param item The item to be removed from the list. + * @return True if item is removed, false if item cannot be found in the list. + */ + public boolean remove(T item) { + return remove(item, true); + } + + /** + * Removes the item at the given index and calls {@link Callback#onRemoved(int, int)}. + * + * @param index The index of the item to be removed. + * @return The removed item. + */ + public T removeItemAt(int index) { + T item = get(index); + removeItemAtIndex(index, true); + return item; + } + + private boolean remove(T item, boolean notify) { + int index = findIndexOf(item, DELETION); + if (index == INVALID_POSITION) { + return false; + } + removeItemAtIndex(index, notify); + return true; + } + + private void removeItemAtIndex(int index, boolean notify) { + System.arraycopy(mData, index + 1, mData, index, mSize - index - 1); + mSize--; + mData[mSize] = null; + if (notify) { + mCallback.onRemoved(index, 1); + } + } + + /** + * Updates the item at the given index and calls {@link Callback#onChanged(int, int)} and/or + * {@link Callback#onMoved(int, int)} if necessary. + *

+ * You can use this method if you need to change an existing Item such that its position in the + * list may change. + *

+ * If the new object is a different object (get(index) != item) and + * {@link Callback#areContentsTheSame(Object, Object)} returns true, SortedList + * avoids calling {@link Callback#onChanged(int, int)} otherwise it calls + * {@link Callback#onChanged(int, int)}. + *

+ * If the new position of the item is different than the provided index, + * SortedList + * calls {@link Callback#onMoved(int, int)}. + * + * @param index The index of the item to replace + * @param item The item to replace the item at the given Index. + * @see #add(Object) + */ + public void updateItemAt(int index, T item) { + final T existing = get(index); + // assume changed if the same object is given back + boolean contentsChanged = existing == item || !mCallback.areContentsTheSame(existing, item); + if (existing != item) { + // different items, we can use comparison and may avoid lookup + final int cmp = mCallback.compare(existing, item); + if (cmp == 0) { + mData[index] = item; + if (contentsChanged) { + mCallback.onChanged(index, 1); + } + return; + } + } + if (contentsChanged) { + mCallback.onChanged(index, 1); + } + // TODO this done in 1 pass to avoid shifting twice. + removeItemAtIndex(index, false); + int newIndex = add(item, false); + if (index != newIndex) { + mCallback.onMoved(index, newIndex); + } + } + + /** + * This method can be used to recalculate the position of the item at the given index, without + * triggering an {@link Callback#onChanged(int, int)} callback. + *

+ * If you are editing objects in the list such that their position in the list may change but + * you don't want to trigger an onChange animation, you can use this method to re-position it. + * If the item changes position, SortedList will call {@link Callback#onMoved(int, int)} + * without + * calling {@link Callback#onChanged(int, int)}. + *

+ * A sample usage may look like: + * + *

+     *     final int position = mSortedList.indexOf(item);
+     *     item.incrementPriority(); // assume items are sorted by priority
+     *     mSortedList.recalculatePositionOfItemAt(position);
+     * 
+ * In the example above, because the sorting criteria of the item has been changed, + * mSortedList.indexOf(item) will not be able to find the item. This is why the code above + * first + * gets the position before editing the item, edits it and informs the SortedList that item + * should be repositioned. + * + * @param index The current index of the Item whose position should be re-calculated. + * @see #updateItemAt(int, Object) + * @see #add(Object) + */ + public void recalculatePositionOfItemAt(int index) { + // TODO can be improved + final T item = get(index); + removeItemAtIndex(index, false); + int newIndex = add(item, false); + if (index != newIndex) { + mCallback.onMoved(index, newIndex); + } + } + + /** + * Returns the item at the given index. + * + * @param index The index of the item to retrieve. + * @return The item at the given index. + * @throws java.lang.IndexOutOfBoundsException if provided index is negative or larger than the + * size of the list. + */ + public T get(int index) throws IndexOutOfBoundsException { + if (index >= mSize || index < 0) { + throw new IndexOutOfBoundsException("Asked to get item at " + index + " but size is " + + mSize); + } + return mData[index]; + } + + /** + * Returns the position of the provided item. + * + * @param item The item to query for position. + * @return The position of the provided item or {@link #INVALID_POSITION} if item is not in the + * list. + */ + public int indexOf(T item) { + return findIndexOf(item, LOOKUP); + } + + private int findIndexOf(T item, int reason) { + int left = 0; + int right = mSize; + while (left < right) { + final int middle = (left + right) / 2; + T myItem = mData[middle]; + final int cmp = mCallback.compare(myItem, item); + if (cmp < 0) { + left = middle + 1; + } else if (cmp == 0) { + if (mCallback.areItemsTheSame(myItem, item)) { + return middle; + } else { + int exact = linearEqualitySearch(item, middle, left, right); + if (reason == INSERTION) { + return exact == INVALID_POSITION ? middle : exact; + } else { + return exact; + } + } + } else { + right = middle; + } + } + return reason == INSERTION ? left : INVALID_POSITION; + } + + private int linearEqualitySearch(T item, int middle, int left, int right) { + // go left + for (int next = middle - 1; next >= left; next--) { + T nextItem = mData[next]; + int cmp = mCallback.compare(nextItem, item); + if (cmp != 0) { + break; + } + if (mCallback.areItemsTheSame(nextItem, item)) { + return next; + } + } + for (int next = middle + 1; next < right; next++) { + T nextItem = mData[next]; + int cmp = mCallback.compare(nextItem, item); + if (cmp != 0) { + break; + } + if (mCallback.areItemsTheSame(nextItem, item)) { + return next; + } + } + return INVALID_POSITION; + } + + private void addToData(int index, T item) { + if (index > mSize) { + throw new IndexOutOfBoundsException( + "cannot add item to " + index + " because size is " + mSize); + } + if (mSize == mData.length) { + // we are at the limit enlarge + T[] newData = (T[]) Array.newInstance(mTClass, mData.length + CAPACITY_GROWTH); + System.arraycopy(mData, 0, newData, 0, index); + newData[index] = item; + System.arraycopy(mData, index, newData, index + 1, mSize - index); + mData = newData; + } else { + // just shift, we fit + System.arraycopy(mData, index, mData, index + 1, mSize - index); + mData[index] = item; + } + mSize++; + } + + /** + * The class that controls the behavior of the {@link SortedList}. + *

+ * It defines how items should be sorted and how duplicates should be handled. + *

+ * SortedList calls the callback methods on this class to notify changes about the underlying + * data. + */ + public static abstract class Callback { + + /** + * Similar to {@link java.util.Comparator#compare(Object, Object)}, should compare two and + * return how they should be ordered. + * + * @param o1 The first object to compare. + * @param o2 The second object to compare. + * @return a negative integer, zero, or a positive integer as the + * first argument is less than, equal to, or greater than the + * second. + */ + abstract public int compare(T2 o1, T2 o2); + + /** + * Called by the SortedList when an item is inserted at the given position. + * + * @param position The position of the new item. + * @param count The number of items that have been added. + */ + abstract public void onInserted(int position, int count); + + /** + * Called by the SortedList when an item is removed from the given position. + * + * @param position The position of the item which has been removed. + * @param count The number of items which have been removed. + */ + abstract public void onRemoved(int position, int count); + + /** + * Called by the SortedList when an item changes its position in the list. + * + * @param fromPosition The previous position of the item before the move. + * @param toPosition The new position of the item. + */ + abstract public void onMoved(int fromPosition, int toPosition); + + /** + * Called by the SortedList when the item at the given position is updated. + * + * @param position The position of the item which has been updated. + * @param count The number of items which has changed. + */ + abstract public void onChanged(int position, int count); + + /** + * Called by the SortedList when it wants to check whether two items have the same data + * or not. SortedList uses this information to decide whether it should call + * {@link #onChanged(int, int)} or not. + *

+ * SortedList uses this method to check equality instead of {@link Object#equals(Object)} + * so + * that you can change its behavior depending on your UI. + *

+ * For example, if you are using SortedList with a {@link android.support.v7.widget.RecyclerView.Adapter + * RecyclerView.Adapter}, you should + * return whether the items' visual representations are the same or not. + * + * @param oldItem The previous representation of the object. + * @param newItem The new object that replaces the previous one. + * @return True if the contents of the items are the same or false if they are different. + */ + abstract public boolean areContentsTheSame(T2 oldItem, T2 newItem); + + /** + * Called by the SortedList to decide whether two object represent the same Item or not. + *

+ * For example, if your items have unique ids, this method should check their equality. + * + * @param item1 The first item to check. + * @param item2 The second item to check. + * @return True if the two items represent the same object or false if they are different. + */ + abstract public boolean areItemsTheSame(T2 item1, T2 item2); + } + + /** + * A callback implementation that can batch notify events dispatched by the SortedList. + *

+ * This class can be useful if you want to do multiple operations on a SortedList but don't + * want to dispatch each event one by one, which may result in a performance issue. + *

+ * For example, if you are going to add multiple items to a SortedList, BatchedCallback call + * convert individual onInserted(index, 1) calls into one + * onInserted(index, N) if items are added into consecutive indices. This change + * can help RecyclerView resolve changes much more easily. + *

+ * If consecutive changes in the SortedList are not suitable for batching, BatchingCallback + * dispatches them as soon as such case is detected. After your edits on the SortedList is + * complete, you must always call {@link BatchedCallback#dispatchLastEvent()} to flush + * all changes to the Callback. + */ + public static class BatchedCallback extends Callback { + + private final Callback mWrappedCallback; + static final int TYPE_NONE = 0; + static final int TYPE_ADD = 1; + static final int TYPE_REMOVE = 2; + static final int TYPE_CHANGE = 3; + static final int TYPE_MOVE = 4; + + int mLastEventType = TYPE_NONE; + int mLastEventPosition = -1; + int mLastEventCount = -1; + + /** + * Creates a new BatchedCallback that wraps the provided Callback. + * + * @param wrappedCallback The Callback which should received the data change callbacks. + * Other method calls (e.g. {@link #compare(Object, Object)} from + * the SortedList are directly forwarded to this Callback. + */ + public BatchedCallback(Callback wrappedCallback) { + mWrappedCallback = wrappedCallback; + } + + @Override + public int compare(T2 o1, T2 o2) { + return mWrappedCallback.compare(o1, o2); + } + + @Override + public void onInserted(int position, int count) { + if (mLastEventType == TYPE_ADD && position >= mLastEventPosition + && position <= mLastEventPosition + mLastEventCount) { + mLastEventCount += count; + mLastEventPosition = Math.min(position, mLastEventPosition); + return; + } + dispatchLastEvent(); + mLastEventPosition = position; + mLastEventCount = count; + mLastEventType = TYPE_ADD; + } + + @Override + public void onRemoved(int position, int count) { + if (mLastEventType == TYPE_REMOVE && mLastEventPosition == position) { + mLastEventCount += count; + return; + } + dispatchLastEvent(); + mLastEventPosition = position; + mLastEventCount = count; + mLastEventType = TYPE_REMOVE; + } + + @Override + public void onMoved(int fromPosition, int toPosition) { + dispatchLastEvent();//moves are not merged + mWrappedCallback.onMoved(fromPosition, toPosition); + } + + @Override + public void onChanged(int position, int count) { + if (mLastEventType == TYPE_CHANGE && + !(position > mLastEventPosition + mLastEventCount + || position + count < mLastEventPosition)) { + // take potential overlap into account + int previousEnd = mLastEventPosition + mLastEventCount; + mLastEventPosition = Math.min(position, mLastEventPosition); + mLastEventCount = Math.max(previousEnd, position + count) - mLastEventPosition; + return; + } + dispatchLastEvent(); + mLastEventPosition = position; + mLastEventCount = count; + mLastEventType = TYPE_CHANGE; + } + + @Override + public boolean areContentsTheSame(T2 oldItem, T2 newItem) { + return mWrappedCallback.areContentsTheSame(oldItem, newItem); + } + + @Override + public boolean areItemsTheSame(T2 item1, T2 item2) { + return mWrappedCallback.areItemsTheSame(item1, item2); + } + + + /** + * This method dispatches any pending event notifications to the wrapped Callback. + * You must always call this method after you are done with editing the SortedList. + */ + public void dispatchLastEvent() { + if (mLastEventType == TYPE_NONE) { + return; + } + switch (mLastEventType) { + case TYPE_ADD: + mWrappedCallback.onInserted(mLastEventPosition, mLastEventCount); + break; + case TYPE_REMOVE: + mWrappedCallback.onRemoved(mLastEventPosition, mLastEventCount); + break; + case TYPE_CHANGE: + mWrappedCallback.onChanged(mLastEventPosition, mLastEventCount); + break; + } + mLastEventType = TYPE_NONE; + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/AdapterHelper.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/AdapterHelper.java new file mode 100644 index 000000000..3b4c28c1a --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/AdapterHelper.java @@ -0,0 +1,733 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.telegram.android.support.widget; + +import android.support.v4.util.Pools; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.telegram.android.support.widget.RecyclerView.*; + +/** + * Helper class that can enqueue and process adapter update operations. + *

+ * To support animations, RecyclerView presents an older version the Adapter to best represent + * previous state of the layout. Sometimes, this is not trivial when items are removed that were + * not laid out, in which case, RecyclerView has no way of providing that item's view for + * animations. + *

+ * AdapterHelper creates an UpdateOp for each adapter data change then pre-processes them. During + * pre processing, AdapterHelper finds out which UpdateOps can be deferred to second layout pass + * and which cannot. For the UpdateOps that cannot be deferred, AdapterHelper will change them + * according to previously deferred operation and dispatch them before the first layout pass. It + * also takes care of updating deferred UpdateOps since order of operations is changed by this + * process. + *

+ * Although operations may be forwarded to LayoutManager in different orders, resulting data set + * is guaranteed to be the consistent. + */ +class AdapterHelper implements OpReorderer.Callback { + + final static int POSITION_TYPE_INVISIBLE = 0; + + final static int POSITION_TYPE_NEW_OR_LAID_OUT = 1; + + private static final boolean DEBUG = false; + + private static final String TAG = "AHT"; + + private Pools.Pool mUpdateOpPool = new Pools.SimplePool(UpdateOp.POOL_SIZE); + + final ArrayList mPendingUpdates = new ArrayList(); + + final ArrayList mPostponedList = new ArrayList(); + + final Callback mCallback; + + Runnable mOnItemProcessedCallback; + + final boolean mDisableRecycler; + + final OpReorderer mOpReorderer; + + AdapterHelper(Callback callback) { + this(callback, false); + } + + AdapterHelper(Callback callback, boolean disableRecycler) { + mCallback = callback; + mDisableRecycler = disableRecycler; + mOpReorderer = new OpReorderer(this); + } + + AdapterHelper addUpdateOp(UpdateOp... ops) { + Collections.addAll(mPendingUpdates, ops); + return this; + } + + void reset() { + recycleUpdateOpsAndClearList(mPendingUpdates); + recycleUpdateOpsAndClearList(mPostponedList); + } + + void preProcess() { + mOpReorderer.reorderOps(mPendingUpdates); + final int count = mPendingUpdates.size(); + for (int i = 0; i < count; i++) { + UpdateOp op = mPendingUpdates.get(i); + switch (op.cmd) { + case UpdateOp.ADD: + applyAdd(op); + break; + case UpdateOp.REMOVE: + applyRemove(op); + break; + case UpdateOp.UPDATE: + applyUpdate(op); + break; + case UpdateOp.MOVE: + applyMove(op); + break; + } + if (mOnItemProcessedCallback != null) { + mOnItemProcessedCallback.run(); + } + } + mPendingUpdates.clear(); + } + + void consumePostponedUpdates() { + final int count = mPostponedList.size(); + for (int i = 0; i < count; i++) { + mCallback.onDispatchSecondPass(mPostponedList.get(i)); + } + recycleUpdateOpsAndClearList(mPostponedList); + } + + private void applyMove(UpdateOp op) { + // MOVE ops are pre-processed so at this point, we know that item is still in the adapter. + // otherwise, it would be converted into a REMOVE operation + postponeAndUpdateViewHolders(op); + } + + private void applyRemove(UpdateOp op) { + int tmpStart = op.positionStart; + int tmpCount = 0; + int tmpEnd = op.positionStart + op.itemCount; + int type = -1; + for (int position = op.positionStart; position < tmpEnd; position++) { + boolean typeChanged = false; + ViewHolder vh = mCallback.findViewHolder(position); + if (vh != null || canFindInPreLayout(position)) { + // If a ViewHolder exists or this is a newly added item, we can defer this update + // to post layout stage. + // * For existing ViewHolders, we'll fake its existence in the pre-layout phase. + // * For items that are added and removed in the same process cycle, they won't + // have any effect in pre-layout since their add ops are already deferred to + // post-layout pass. + if (type == POSITION_TYPE_INVISIBLE) { + // Looks like we have other updates that we cannot merge with this one. + // Create an UpdateOp and dispatch it to LayoutManager. + UpdateOp newOp = obtainUpdateOp(UpdateOp.REMOVE, tmpStart, tmpCount); + dispatchAndUpdateViewHolders(newOp); + typeChanged = true; + } + type = POSITION_TYPE_NEW_OR_LAID_OUT; + } else { + // This update cannot be recovered because we don't have a ViewHolder representing + // this position. Instead, post it to LayoutManager immediately + if (type == POSITION_TYPE_NEW_OR_LAID_OUT) { + // Looks like we have other updates that we cannot merge with this one. + // Create UpdateOp op and dispatch it to LayoutManager. + UpdateOp newOp = obtainUpdateOp(UpdateOp.REMOVE, tmpStart, tmpCount); + postponeAndUpdateViewHolders(newOp); + typeChanged = true; + } + type = POSITION_TYPE_INVISIBLE; + } + if (typeChanged) { + position -= tmpCount; // also equal to tmpStart + tmpEnd -= tmpCount; + tmpCount = 1; + } else { + tmpCount++; + } + } + if (tmpCount != op.itemCount) { // all 1 effect + recycleUpdateOp(op); + op = obtainUpdateOp(UpdateOp.REMOVE, tmpStart, tmpCount); + } + if (type == POSITION_TYPE_INVISIBLE) { + dispatchAndUpdateViewHolders(op); + } else { + postponeAndUpdateViewHolders(op); + } + } + + private void applyUpdate(UpdateOp op) { + int tmpStart = op.positionStart; + int tmpCount = 0; + int tmpEnd = op.positionStart + op.itemCount; + int type = -1; + for (int position = op.positionStart; position < tmpEnd; position++) { + ViewHolder vh = mCallback.findViewHolder(position); + if (vh != null || canFindInPreLayout(position)) { // deferred + if (type == POSITION_TYPE_INVISIBLE) { + UpdateOp newOp = obtainUpdateOp(UpdateOp.UPDATE, tmpStart, tmpCount); + dispatchAndUpdateViewHolders(newOp); + tmpCount = 0; + tmpStart = position; + } + type = POSITION_TYPE_NEW_OR_LAID_OUT; + } else { // applied + if (type == POSITION_TYPE_NEW_OR_LAID_OUT) { + UpdateOp newOp = obtainUpdateOp(UpdateOp.UPDATE, tmpStart, tmpCount); + postponeAndUpdateViewHolders(newOp); + tmpCount = 0; + tmpStart = position; + } + type = POSITION_TYPE_INVISIBLE; + } + tmpCount++; + } + if (tmpCount != op.itemCount) { // all 1 effect + recycleUpdateOp(op); + op = obtainUpdateOp(UpdateOp.UPDATE, tmpStart, tmpCount); + } + if (type == POSITION_TYPE_INVISIBLE) { + dispatchAndUpdateViewHolders(op); + } else { + postponeAndUpdateViewHolders(op); + } + } + + private void dispatchAndUpdateViewHolders(UpdateOp op) { + // tricky part. + // traverse all postpones and revert their changes on this op if necessary, apply updated + // dispatch to them since now they are after this op. + if (op.cmd == UpdateOp.ADD || op.cmd == UpdateOp.MOVE) { + throw new IllegalArgumentException("should not dispatch add or move for pre layout"); + } + if (DEBUG) { + Log.d(TAG, "dispatch (pre)" + op); + Log.d(TAG, "postponed state before:"); + for (UpdateOp updateOp : mPostponedList) { + Log.d(TAG, updateOp.toString()); + } + Log.d(TAG, "----"); + } + + // handle each pos 1 by 1 to ensure continuity. If it breaks, dispatch partial + // TODO Since move ops are pushed to end, we should not need this anymore + int tmpStart = updatePositionWithPostponed(op.positionStart, op.cmd); + if (DEBUG) { + Log.d(TAG, "pos:" + op.positionStart + ",updatedPos:" + tmpStart); + } + int tmpCnt = 1; + int offsetPositionForPartial = op.positionStart; + final int positionMultiplier; + switch (op.cmd) { + case UpdateOp.UPDATE: + positionMultiplier = 1; + break; + case UpdateOp.REMOVE: + positionMultiplier = 0; + break; + default: + throw new IllegalArgumentException("op should be remove or update." + op); + } + for (int p = 1; p < op.itemCount; p++) { + final int pos = op.positionStart + (positionMultiplier * p); + int updatedPos = updatePositionWithPostponed(pos, op.cmd); + if (DEBUG) { + Log.d(TAG, "pos:" + pos + ",updatedPos:" + updatedPos); + } + boolean continuous = false; + switch (op.cmd) { + case UpdateOp.UPDATE: + continuous = updatedPos == tmpStart + 1; + break; + case UpdateOp.REMOVE: + continuous = updatedPos == tmpStart; + break; + } + if (continuous) { + tmpCnt++; + } else { + // need to dispatch this separately + UpdateOp tmp = obtainUpdateOp(op.cmd, tmpStart, tmpCnt); + if (DEBUG) { + Log.d(TAG, "need to dispatch separately " + tmp); + } + dispatchFirstPassAndUpdateViewHolders(tmp, offsetPositionForPartial); + recycleUpdateOp(tmp); + if (op.cmd == UpdateOp.UPDATE) { + offsetPositionForPartial += tmpCnt; + } + tmpStart = updatedPos;// need to remove previously dispatched + tmpCnt = 1; + } + } + recycleUpdateOp(op); + if (tmpCnt > 0) { + UpdateOp tmp = obtainUpdateOp(op.cmd, tmpStart, tmpCnt); + if (DEBUG) { + Log.d(TAG, "dispatching:" + tmp); + } + dispatchFirstPassAndUpdateViewHolders(tmp, offsetPositionForPartial); + recycleUpdateOp(tmp); + } + if (DEBUG) { + Log.d(TAG, "post dispatch"); + Log.d(TAG, "postponed state after:"); + for (UpdateOp updateOp : mPostponedList) { + Log.d(TAG, updateOp.toString()); + } + Log.d(TAG, "----"); + } + } + + void dispatchFirstPassAndUpdateViewHolders(UpdateOp op, int offsetStart) { + mCallback.onDispatchFirstPass(op); + switch (op.cmd) { + case UpdateOp.REMOVE: + mCallback.offsetPositionsForRemovingInvisible(offsetStart, op.itemCount); + break; + case UpdateOp.UPDATE: + mCallback.markViewHoldersUpdated(offsetStart, op.itemCount); + break; + default: + throw new IllegalArgumentException("only remove and update ops can be dispatched" + + " in first pass"); + } + } + + private int updatePositionWithPostponed(int pos, int cmd) { + final int count = mPostponedList.size(); + for (int i = count - 1; i >= 0; i--) { + UpdateOp postponed = mPostponedList.get(i); + if (postponed.cmd == UpdateOp.MOVE) { + int start, end; + if (postponed.positionStart < postponed.itemCount) { + start = postponed.positionStart; + end = postponed.itemCount; + } else { + start = postponed.itemCount; + end = postponed.positionStart; + } + if (pos >= start && pos <= end) { + //i'm affected + if (start == postponed.positionStart) { + if (cmd == UpdateOp.ADD) { + postponed.itemCount++; + } else if (cmd == UpdateOp.REMOVE) { + postponed.itemCount--; + } + // op moved to left, move it right to revert + pos++; + } else { + if (cmd == UpdateOp.ADD) { + postponed.positionStart++; + } else if (cmd == UpdateOp.REMOVE) { + postponed.positionStart--; + } + // op was moved right, move left to revert + pos--; + } + } else if (pos < postponed.positionStart) { + // postponed MV is outside the dispatched OP. if it is before, offset + if (cmd == UpdateOp.ADD) { + postponed.positionStart++; + postponed.itemCount++; + } else if (cmd == UpdateOp.REMOVE) { + postponed.positionStart--; + postponed.itemCount--; + } + } + } else { + if (postponed.positionStart <= pos) { + if (postponed.cmd == UpdateOp.ADD) { + pos -= postponed.itemCount; + } else if (postponed.cmd == UpdateOp.REMOVE) { + pos += postponed.itemCount; + } + } else { + if (cmd == UpdateOp.ADD) { + postponed.positionStart++; + } else if (cmd == UpdateOp.REMOVE) { + postponed.positionStart--; + } + } + } + if (DEBUG) { + Log.d(TAG, "dispath (step" + i + ")"); + Log.d(TAG, "postponed state:" + i + ", pos:" + pos); + for (UpdateOp updateOp : mPostponedList) { + Log.d(TAG, updateOp.toString()); + } + Log.d(TAG, "----"); + } + } + for (int i = mPostponedList.size() - 1; i >= 0; i--) { + UpdateOp op = mPostponedList.get(i); + if (op.cmd == UpdateOp.MOVE) { + if (op.itemCount == op.positionStart || op.itemCount < 0) { + mPostponedList.remove(i); + recycleUpdateOp(op); + } + } else if (op.itemCount <= 0) { + mPostponedList.remove(i); + recycleUpdateOp(op); + } + } + return pos; + } + + private boolean canFindInPreLayout(int position) { + final int count = mPostponedList.size(); + for (int i = 0; i < count; i++) { + UpdateOp op = mPostponedList.get(i); + if (op.cmd == UpdateOp.MOVE) { + if (findPositionOffset(op.itemCount, i + 1) == position) { + return true; + } + } else if (op.cmd == UpdateOp.ADD) { + // TODO optimize. + final int end = op.positionStart + op.itemCount; + for (int pos = op.positionStart; pos < end; pos++) { + if (findPositionOffset(pos, i + 1) == position) { + return true; + } + } + } + } + return false; + } + + private void applyAdd(UpdateOp op) { + postponeAndUpdateViewHolders(op); + } + + private void postponeAndUpdateViewHolders(UpdateOp op) { + if (DEBUG) { + Log.d(TAG, "postponing " + op); + } + mPostponedList.add(op); + switch (op.cmd) { + case UpdateOp.ADD: + mCallback.offsetPositionsForAdd(op.positionStart, op.itemCount); + break; + case UpdateOp.MOVE: + mCallback.offsetPositionsForMove(op.positionStart, op.itemCount); + break; + case UpdateOp.REMOVE: + mCallback.offsetPositionsForRemovingLaidOutOrNewView(op.positionStart, + op.itemCount); + break; + case UpdateOp.UPDATE: + mCallback.markViewHoldersUpdated(op.positionStart, op.itemCount); + break; + default: + throw new IllegalArgumentException("Unknown update op type for " + op); + } + } + + boolean hasPendingUpdates() { + return mPendingUpdates.size() > 0; + } + + int findPositionOffset(int position) { + return findPositionOffset(position, 0); + } + + int findPositionOffset(int position, int firstPostponedItem) { + int count = mPostponedList.size(); + for (int i = firstPostponedItem; i < count; ++i) { + UpdateOp op = mPostponedList.get(i); + if (op.cmd == UpdateOp.MOVE) { + if (op.positionStart == position) { + position = op.itemCount; + } else { + if (op.positionStart < position) { + position--; // like a remove + } + if (op.itemCount <= position) { + position++; // like an add + } + } + } else if (op.positionStart <= position) { + if (op.cmd == UpdateOp.REMOVE) { + if (position < op.positionStart + op.itemCount) { + return -1; + } + position -= op.itemCount; + } else if (op.cmd == UpdateOp.ADD) { + position += op.itemCount; + } + } + } + return position; + } + + /** + * @return True if updates should be processed. + */ + boolean onItemRangeChanged(int positionStart, int itemCount) { + mPendingUpdates.add(obtainUpdateOp(UpdateOp.UPDATE, positionStart, itemCount)); + return mPendingUpdates.size() == 1; + } + + /** + * @return True if updates should be processed. + */ + boolean onItemRangeInserted(int positionStart, int itemCount) { + mPendingUpdates.add(obtainUpdateOp(UpdateOp.ADD, positionStart, itemCount)); + return mPendingUpdates.size() == 1; + } + + /** + * @return True if updates should be processed. + */ + boolean onItemRangeRemoved(int positionStart, int itemCount) { + mPendingUpdates.add(obtainUpdateOp(UpdateOp.REMOVE, positionStart, itemCount)); + return mPendingUpdates.size() == 1; + } + + /** + * @return True if updates should be processed. + */ + boolean onItemRangeMoved(int from, int to, int itemCount) { + if (from == to) { + return false;//no-op + } + if (itemCount != 1) { + throw new IllegalArgumentException("Moving more than 1 item is not supported yet"); + } + mPendingUpdates.add(obtainUpdateOp(UpdateOp.MOVE, from, to)); + return mPendingUpdates.size() == 1; + } + + /** + * Skips pre-processing and applies all updates in one pass. + */ + void consumeUpdatesInOnePass() { + // we still consume postponed updates (if there is) in case there was a pre-process call + // w/o a matching consumePostponedUpdates. + consumePostponedUpdates(); + final int count = mPendingUpdates.size(); + for (int i = 0; i < count; i++) { + UpdateOp op = mPendingUpdates.get(i); + switch (op.cmd) { + case UpdateOp.ADD: + mCallback.onDispatchSecondPass(op); + mCallback.offsetPositionsForAdd(op.positionStart, op.itemCount); + break; + case UpdateOp.REMOVE: + mCallback.onDispatchSecondPass(op); + mCallback.offsetPositionsForRemovingInvisible(op.positionStart, op.itemCount); + break; + case UpdateOp.UPDATE: + mCallback.onDispatchSecondPass(op); + mCallback.markViewHoldersUpdated(op.positionStart, op.itemCount); + break; + case UpdateOp.MOVE: + mCallback.onDispatchSecondPass(op); + mCallback.offsetPositionsForMove(op.positionStart, op.itemCount); + break; + } + if (mOnItemProcessedCallback != null) { + mOnItemProcessedCallback.run(); + } + } + recycleUpdateOpsAndClearList(mPendingUpdates); + } + + public int applyPendingUpdatesToPosition(int position) { + final int size = mPendingUpdates.size(); + for (int i = 0; i < size; i ++) { + UpdateOp op = mPendingUpdates.get(i); + switch (op.cmd) { + case UpdateOp.ADD: + if (op.positionStart <= position) { + position += op.itemCount; + } + break; + case UpdateOp.REMOVE: + if (op.positionStart <= position) { + final int end = op.positionStart + op.itemCount; + if (end > position) { + return RecyclerView.NO_POSITION; + } + position -= op.itemCount; + } + break; + case UpdateOp.MOVE: + if (op.positionStart == position) { + position = op.itemCount;//position end + } else { + if (op.positionStart < position) { + position -= 1; + } + if (op.itemCount <= position) { + position += 1; + } + } + break; + } + } + return position; + } + + /** + * Queued operation to happen when child views are updated. + */ + static class UpdateOp { + + static final int ADD = 0; + + static final int REMOVE = 1; + + static final int UPDATE = 2; + + static final int MOVE = 3; + + static final int POOL_SIZE = 30; + + int cmd; + + int positionStart; + + // holds the target position if this is a MOVE + int itemCount; + + UpdateOp(int cmd, int positionStart, int itemCount) { + this.cmd = cmd; + this.positionStart = positionStart; + this.itemCount = itemCount; + } + + String cmdToString() { + switch (cmd) { + case ADD: + return "add"; + case REMOVE: + return "rm"; + case UPDATE: + return "up"; + case MOVE: + return "mv"; + } + return "??"; + } + + @Override + public String toString() { + return "[" + cmdToString() + ",s:" + positionStart + "c:" + itemCount + "]"; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + UpdateOp op = (UpdateOp) o; + + if (cmd != op.cmd) { + return false; + } + if (cmd == MOVE && Math.abs(itemCount - positionStart) == 1) { + // reverse of this is also true + if (itemCount == op.positionStart && positionStart == op.itemCount) { + return true; + } + } + if (itemCount != op.itemCount) { + return false; + } + if (positionStart != op.positionStart) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + int result = cmd; + result = 31 * result + positionStart; + result = 31 * result + itemCount; + return result; + } + } + + @Override + public UpdateOp obtainUpdateOp(int cmd, int positionStart, int itemCount) { + UpdateOp op = mUpdateOpPool.acquire(); + if (op == null) { + op = new UpdateOp(cmd, positionStart, itemCount); + } else { + op.cmd = cmd; + op.positionStart = positionStart; + op.itemCount = itemCount; + } + return op; + } + + @Override + public void recycleUpdateOp(UpdateOp op) { + if (!mDisableRecycler) { + mUpdateOpPool.release(op); + } + } + + void recycleUpdateOpsAndClearList(List ops) { + final int count = ops.size(); + for (int i = 0; i < count; i++) { + recycleUpdateOp(ops.get(i)); + } + ops.clear(); + } + + /** + * Contract between AdapterHelper and RecyclerView. + */ + static interface Callback { + + ViewHolder findViewHolder(int position); + + void offsetPositionsForRemovingInvisible(int positionStart, int itemCount); + + void offsetPositionsForRemovingLaidOutOrNewView(int positionStart, int itemCount); + + void markViewHoldersUpdated(int positionStart, int itemCount); + + void onDispatchFirstPass(UpdateOp updateOp); + + void onDispatchSecondPass(UpdateOp updateOp); + + void offsetPositionsForAdd(int positionStart, int itemCount); + + void offsetPositionsForMove(int from, int to); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/ChildHelper.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/ChildHelper.java new file mode 100644 index 000000000..838bca966 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/ChildHelper.java @@ -0,0 +1,487 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.telegram.android.support.widget; + +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; + +import java.util.ArrayList; +import java.util.List; + +/** + * Helper class to manage children. + *

+ * It wraps a RecyclerView and adds ability to hide some children. There are two sets of methods + * provided by this class. Regular methods are the ones that replicate ViewGroup methods + * like getChildAt, getChildCount etc. These methods ignore hidden children. + *

+ * When RecyclerView needs direct access to the view group children, it can call unfiltered + * methods like get getUnfilteredChildCount or getUnfilteredChildAt. + */ +class ChildHelper { + + private static final boolean DEBUG = false; + + private static final String TAG = "ChildrenHelper"; + + final Callback mCallback; + + final Bucket mBucket; + + final List mHiddenViews; + + ChildHelper(Callback callback) { + mCallback = callback; + mBucket = new Bucket(); + mHiddenViews = new ArrayList(); + } + + /** + * Adds a view to the ViewGroup + * + * @param child View to add. + * @param hidden If set to true, this item will be invisible from regular methods. + */ + void addView(View child, boolean hidden) { + addView(child, -1, hidden); + } + + /** + * Add a view to the ViewGroup at an index + * + * @param child View to add. + * @param index Index of the child from the regular perspective (excluding hidden views). + * ChildHelper offsets this index to actual ViewGroup index. + * @param hidden If set to true, this item will be invisible from regular methods. + */ + void addView(View child, int index, boolean hidden) { + final int offset; + if (index < 0) { + offset = mCallback.getChildCount(); + } else { + offset = getOffset(index); + } + mBucket.insert(offset, hidden); + if (hidden) { + mHiddenViews.add(child); + } + mCallback.addView(child, offset); + if (DEBUG) { + Log.d(TAG, "addViewAt " + index + ",h:" + hidden + ", " + this); + } + } + + private int getOffset(int index) { + if (index < 0) { + return -1; //anything below 0 won't work as diff will be undefined. + } + final int limit = mCallback.getChildCount(); + int offset = index; + while (offset < limit) { + final int removedBefore = mBucket.countOnesBefore(offset); + final int diff = index - (offset - removedBefore); + if (diff == 0) { + while (mBucket.get(offset)) { // ensure this offset is not hidden + offset ++; + } + return offset; + } else { + offset += diff; + } + } + return -1; + } + + /** + * Removes the provided View from underlying RecyclerView. + * + * @param view The view to remove. + */ + void removeView(View view) { + int index = mCallback.indexOfChild(view); + if (index < 0) { + return; + } + if (mBucket.remove(index)) { + mHiddenViews.remove(view); + } + mCallback.removeViewAt(index); + if (DEBUG) { + Log.d(TAG, "remove View off:" + index + "," + this); + } + } + + /** + * Removes the view at the provided index from RecyclerView. + * + * @param index Index of the child from the regular perspective (excluding hidden views). + * ChildHelper offsets this index to actual ViewGroup index. + */ + void removeViewAt(int index) { + final int offset = getOffset(index); + final View view = mCallback.getChildAt(offset); + if (view == null) { + return; + } + if (mBucket.remove(offset)) { + mHiddenViews.remove(view); + } + mCallback.removeViewAt(offset); + if (DEBUG) { + Log.d(TAG, "removeViewAt " + index + ", off:" + offset + ", " + this); + } + } + + /** + * Returns the child at provided index. + * + * @param index Index of the child to return in regular perspective. + */ + View getChildAt(int index) { + final int offset = getOffset(index); + return mCallback.getChildAt(offset); + } + + /** + * Removes all views from the ViewGroup including the hidden ones. + */ + void removeAllViewsUnfiltered() { + mBucket.reset(); + mHiddenViews.clear(); + mCallback.removeAllViews(); + if (DEBUG) { + Log.d(TAG, "removeAllViewsUnfiltered"); + } + } + + /** + * This can be used to find a disappearing view by position. + * + * @param position The adapter position of the item. + * @param type View type, can be {@link RecyclerView#INVALID_TYPE}. + * @return A hidden view with a valid ViewHolder that matches the position and type. + */ + View findHiddenNonRemovedView(int position, int type) { + final int count = mHiddenViews.size(); + for (int i = 0; i < count; i++) { + final View view = mHiddenViews.get(i); + RecyclerView.ViewHolder holder = mCallback.getChildViewHolder(view); + if (holder.getLayoutPosition() == position && !holder.isInvalid() && + (type == RecyclerView.INVALID_TYPE || holder.getItemViewType() == type)) { + return view; + } + } + return null; + } + + /** + * Attaches the provided view to the underlying ViewGroup. + * + * @param child Child to attach. + * @param index Index of the child to attach in regular perspective. + * @param layoutParams LayoutParams for the child. + * @param hidden If set to true, this item will be invisible to the regular methods. + */ + void attachViewToParent(View child, int index, ViewGroup.LayoutParams layoutParams, + boolean hidden) { + final int offset; + if (index < 0) { + offset = mCallback.getChildCount(); + } else { + offset = getOffset(index); + } + mBucket.insert(offset, hidden); + if (hidden) { + mHiddenViews.add(child); + } + mCallback.attachViewToParent(child, offset, layoutParams); + if (DEBUG) { + Log.d(TAG, "attach view to parent index:" + index + ",off:" + offset + "," + + "h:" + hidden + ", " + this); + } + } + + /** + * Returns the number of children that are not hidden. + * + * @return Number of children that are not hidden. + * @see #getChildAt(int) + */ + int getChildCount() { + return mCallback.getChildCount() - mHiddenViews.size(); + } + + /** + * Returns the total number of children. + * + * @return The total number of children including the hidden views. + * @see #getUnfilteredChildAt(int) + */ + int getUnfilteredChildCount() { + return mCallback.getChildCount(); + } + + /** + * Returns a child by ViewGroup offset. ChildHelper won't offset this index. + * + * @param index ViewGroup index of the child to return. + * @return The view in the provided index. + */ + View getUnfilteredChildAt(int index) { + return mCallback.getChildAt(index); + } + + /** + * Detaches the view at the provided index. + * + * @param index Index of the child to return in regular perspective. + */ + void detachViewFromParent(int index) { + final int offset = getOffset(index); + mBucket.remove(offset); + mCallback.detachViewFromParent(offset); + if (DEBUG) { + Log.d(TAG, "detach view from parent " + index + ", off:" + offset); + } + } + + /** + * Returns the index of the child in regular perspective. + * + * @param child The child whose index will be returned. + * @return The regular perspective index of the child or -1 if it does not exists. + */ + int indexOfChild(View child) { + final int index = mCallback.indexOfChild(child); + if (index == -1) { + return -1; + } + if (mBucket.get(index)) { + if (DEBUG) { + throw new IllegalArgumentException("cannot get index of a hidden child"); + } else { + return -1; + } + } + // reverse the index + return index - mBucket.countOnesBefore(index); + } + + /** + * Returns whether a View is visible to LayoutManager or not. + * + * @param view The child view to check. Should be a child of the Callback. + * @return True if the View is not visible to LayoutManager + */ + boolean isHidden(View view) { + return mHiddenViews.contains(view); + } + + /** + * Marks a child view as hidden. + * + * @param view The view to hide. + */ + void hide(View view) { + final int offset = mCallback.indexOfChild(view); + if (offset < 0) { + throw new IllegalArgumentException("view is not a child, cannot hide " + view); + } + if (DEBUG && mBucket.get(offset)) { + throw new RuntimeException("trying to hide same view twice, how come ? " + view); + } + mBucket.set(offset); + mHiddenViews.add(view); + if (DEBUG) { + Log.d(TAG, "hiding child " + view + " at offset " + offset+ ", " + this); + } + } + + @Override + public String toString() { + return mBucket.toString() + ", hidden list:" + mHiddenViews.size(); + } + + /** + * Removes a view from the ViewGroup if it is hidden. + * + * @param view The view to remove. + * @return True if the View is found and it is hidden. False otherwise. + */ + boolean removeViewIfHidden(View view) { + final int index = mCallback.indexOfChild(view); + if (index == -1) { + if (mHiddenViews.remove(view) && DEBUG) { + throw new IllegalStateException("view is in hidden list but not in view group"); + } + return true; + } + if (mBucket.get(index)) { + mBucket.remove(index); + if (!mHiddenViews.remove(view) && DEBUG) { + throw new IllegalStateException( + "removed a hidden view but it is not in hidden views list"); + } + mCallback.removeViewAt(index); + return true; + } + return false; + } + + /** + * Bitset implementation that provides methods to offset indices. + */ + static class Bucket { + + final static int BITS_PER_WORD = Long.SIZE; + + final static long LAST_BIT = 1L << (Long.SIZE - 1); + + long mData = 0; + + Bucket next; + + void set(int index) { + if (index >= BITS_PER_WORD) { + ensureNext(); + next.set(index - BITS_PER_WORD); + } else { + mData |= 1L << index; + } + } + + private void ensureNext() { + if (next == null) { + next = new Bucket(); + } + } + + void clear(int index) { + if (index >= BITS_PER_WORD) { + if (next != null) { + next.clear(index - BITS_PER_WORD); + } + } else { + mData &= ~(1L << index); + } + + } + + boolean get(int index) { + if (index >= BITS_PER_WORD) { + ensureNext(); + return next.get(index - BITS_PER_WORD); + } else { + return (mData & (1L << index)) != 0; + } + } + + void reset() { + mData = 0; + if (next != null) { + next.reset(); + } + } + + void insert(int index, boolean value) { + if (index >= BITS_PER_WORD) { + ensureNext(); + next.insert(index - BITS_PER_WORD, value); + } else { + final boolean lastBit = (mData & LAST_BIT) != 0; + long mask = (1L << index) - 1; + final long before = mData & mask; + final long after = ((mData & ~mask)) << 1; + mData = before | after; + if (value) { + set(index); + } else { + clear(index); + } + if (lastBit || next != null) { + ensureNext(); + next.insert(0, lastBit); + } + } + } + + boolean remove(int index) { + if (index >= BITS_PER_WORD) { + ensureNext(); + return next.remove(index - BITS_PER_WORD); + } else { + long mask = (1L << index); + final boolean value = (mData & mask) != 0; + mData &= ~mask; + mask = mask - 1; + final long before = mData & mask; + // cannot use >> because it adds one. + final long after = Long.rotateRight(mData & ~mask, 1); + mData = before | after; + if (next != null) { + if (next.get(0)) { + set(BITS_PER_WORD - 1); + } + next.remove(0); + } + return value; + } + } + + int countOnesBefore(int index) { + if (next == null) { + if (index >= BITS_PER_WORD) { + return Long.bitCount(mData); + } + return Long.bitCount(mData & ((1L << index) - 1)); + } + if (index < BITS_PER_WORD) { + return Long.bitCount(mData & ((1L << index) - 1)); + } else { + return next.countOnesBefore(index - BITS_PER_WORD) + Long.bitCount(mData); + } + } + + @Override + public String toString() { + return next == null ? Long.toBinaryString(mData) + : next.toString() + "xx" + Long.toBinaryString(mData); + } + } + + static interface Callback { + + int getChildCount(); + + void addView(View child, int index); + + int indexOfChild(View view); + + void removeViewAt(int index); + + View getChildAt(int offset); + + void removeAllViews(); + + RecyclerView.ViewHolder getChildViewHolder(View view); + + void attachViewToParent(View child, int index, ViewGroup.LayoutParams layoutParams); + + void detachViewFromParent(int offset); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/DefaultItemAnimator.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/DefaultItemAnimator.java new file mode 100644 index 000000000..bbbc20a54 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/DefaultItemAnimator.java @@ -0,0 +1,631 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.telegram.android.support.widget; + +import android.support.v4.view.ViewCompat; +import android.support.v4.view.ViewPropertyAnimatorCompat; +import android.support.v4.view.ViewPropertyAnimatorListener; +import org.telegram.android.support.widget.RecyclerView.ViewHolder; +import android.view.View; + +import java.util.ArrayList; +import java.util.List; + +/** + * This implementation of {@link RecyclerView.ItemAnimator} provides basic + * animations on remove, add, and move events that happen to the items in + * a RecyclerView. RecyclerView uses a DefaultItemAnimator by default. + * + * @see RecyclerView#setItemAnimator(RecyclerView.ItemAnimator) + */ +public class DefaultItemAnimator extends RecyclerView.ItemAnimator { + private static final boolean DEBUG = false; + + private ArrayList mPendingRemovals = new ArrayList(); + private ArrayList mPendingAdditions = new ArrayList(); + private ArrayList mPendingMoves = new ArrayList(); + private ArrayList mPendingChanges = new ArrayList(); + + private ArrayList> mAdditionsList = + new ArrayList>(); + private ArrayList> mMovesList = new ArrayList>(); + private ArrayList> mChangesList = new ArrayList>(); + + private ArrayList mAddAnimations = new ArrayList(); + private ArrayList mMoveAnimations = new ArrayList(); + private ArrayList mRemoveAnimations = new ArrayList(); + private ArrayList mChangeAnimations = new ArrayList(); + + private static class MoveInfo { + public ViewHolder holder; + public int fromX, fromY, toX, toY; + + private MoveInfo(ViewHolder holder, int fromX, int fromY, int toX, int toY) { + this.holder = holder; + this.fromX = fromX; + this.fromY = fromY; + this.toX = toX; + this.toY = toY; + } + } + + private static class ChangeInfo { + public ViewHolder oldHolder, newHolder; + public int fromX, fromY, toX, toY; + private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder) { + this.oldHolder = oldHolder; + this.newHolder = newHolder; + } + + private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder, + int fromX, int fromY, int toX, int toY) { + this(oldHolder, newHolder); + this.fromX = fromX; + this.fromY = fromY; + this.toX = toX; + this.toY = toY; + } + + @Override + public String toString() { + return "ChangeInfo{" + + "oldHolder=" + oldHolder + + ", newHolder=" + newHolder + + ", fromX=" + fromX + + ", fromY=" + fromY + + ", toX=" + toX + + ", toY=" + toY + + '}'; + } + } + + @Override + public void runPendingAnimations() { + boolean removalsPending = !mPendingRemovals.isEmpty(); + boolean movesPending = !mPendingMoves.isEmpty(); + boolean changesPending = !mPendingChanges.isEmpty(); + boolean additionsPending = !mPendingAdditions.isEmpty(); + if (!removalsPending && !movesPending && !additionsPending && !changesPending) { + // nothing to animate + return; + } + // First, remove stuff + for (ViewHolder holder : mPendingRemovals) { + animateRemoveImpl(holder); + } + mPendingRemovals.clear(); + // Next, move stuff + if (movesPending) { + final ArrayList moves = new ArrayList(); + moves.addAll(mPendingMoves); + mMovesList.add(moves); + mPendingMoves.clear(); + Runnable mover = new Runnable() { + @Override + public void run() { + for (MoveInfo moveInfo : moves) { + animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY, + moveInfo.toX, moveInfo.toY); + } + moves.clear(); + mMovesList.remove(moves); + } + }; + if (removalsPending) { + View view = moves.get(0).holder.itemView; + ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration()); + } else { + mover.run(); + } + } + // Next, change stuff, to run in parallel with move animations + if (changesPending) { + final ArrayList changes = new ArrayList(); + changes.addAll(mPendingChanges); + mChangesList.add(changes); + mPendingChanges.clear(); + Runnable changer = new Runnable() { + @Override + public void run() { + for (ChangeInfo change : changes) { + animateChangeImpl(change); + } + changes.clear(); + mChangesList.remove(changes); + } + }; + if (removalsPending) { + ViewHolder holder = changes.get(0).oldHolder; + ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration()); + } else { + changer.run(); + } + } + // Next, add stuff + if (additionsPending) { + final ArrayList additions = new ArrayList(); + additions.addAll(mPendingAdditions); + mAdditionsList.add(additions); + mPendingAdditions.clear(); + Runnable adder = new Runnable() { + public void run() { + for (ViewHolder holder : additions) { + animateAddImpl(holder); + } + additions.clear(); + mAdditionsList.remove(additions); + } + }; + if (removalsPending || movesPending || changesPending) { + long removeDuration = removalsPending ? getRemoveDuration() : 0; + long moveDuration = movesPending ? getMoveDuration() : 0; + long changeDuration = changesPending ? getChangeDuration() : 0; + long totalDelay = removeDuration + Math.max(moveDuration, changeDuration); + View view = additions.get(0).itemView; + ViewCompat.postOnAnimationDelayed(view, adder, totalDelay); + } else { + adder.run(); + } + } + } + + @Override + public boolean animateRemove(final ViewHolder holder) { + endAnimation(holder); + mPendingRemovals.add(holder); + return true; + } + + private void animateRemoveImpl(final ViewHolder holder) { + final View view = holder.itemView; + final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); + mRemoveAnimations.add(holder); + animation.setDuration(getRemoveDuration()) + .alpha(0).setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchRemoveStarting(holder); + } + + @Override + public void onAnimationEnd(View view) { + animation.setListener(null); + ViewCompat.setAlpha(view, 1); + dispatchRemoveFinished(holder); + mRemoveAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + @Override + public boolean animateAdd(final ViewHolder holder) { + endAnimation(holder); + ViewCompat.setAlpha(holder.itemView, 0); + mPendingAdditions.add(holder); + return true; + } + + private void animateAddImpl(final ViewHolder holder) { + final View view = holder.itemView; + final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); + mAddAnimations.add(holder); + animation.alpha(1).setDuration(getAddDuration()). + setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchAddStarting(holder); + } + @Override + public void onAnimationCancel(View view) { + ViewCompat.setAlpha(view, 1); + } + + @Override + public void onAnimationEnd(View view) { + animation.setListener(null); + dispatchAddFinished(holder); + mAddAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + @Override + public boolean animateMove(final ViewHolder holder, int fromX, int fromY, + int toX, int toY) { + final View view = holder.itemView; + fromX += ViewCompat.getTranslationX(holder.itemView); + fromY += ViewCompat.getTranslationY(holder.itemView); + endAnimation(holder); + int deltaX = toX - fromX; + int deltaY = toY - fromY; + if (deltaX == 0 && deltaY == 0) { + dispatchMoveFinished(holder); + return false; + } + if (deltaX != 0) { + ViewCompat.setTranslationX(view, -deltaX); + } + if (deltaY != 0) { + ViewCompat.setTranslationY(view, -deltaY); + } + mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY)); + return true; + } + + private void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) { + final View view = holder.itemView; + final int deltaX = toX - fromX; + final int deltaY = toY - fromY; + if (deltaX != 0) { + ViewCompat.animate(view).translationX(0); + } + if (deltaY != 0) { + ViewCompat.animate(view).translationY(0); + } + // TODO: make EndActions end listeners instead, since end actions aren't called when + // vpas are canceled (and can't end them. why?) + // need listener functionality in VPACompat for this. Ick. + final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); + mMoveAnimations.add(holder); + animation.setDuration(getMoveDuration()).setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchMoveStarting(holder); + } + @Override + public void onAnimationCancel(View view) { + if (deltaX != 0) { + ViewCompat.setTranslationX(view, 0); + } + if (deltaY != 0) { + ViewCompat.setTranslationY(view, 0); + } + } + @Override + public void onAnimationEnd(View view) { + animation.setListener(null); + dispatchMoveFinished(holder); + mMoveAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + @Override + public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder, + int fromX, int fromY, int toX, int toY) { + final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView); + final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView); + final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView); + endAnimation(oldHolder); + int deltaX = (int) (toX - fromX - prevTranslationX); + int deltaY = (int) (toY - fromY - prevTranslationY); + // recover prev translation state after ending animation + ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX); + ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY); + ViewCompat.setAlpha(oldHolder.itemView, prevAlpha); + if (newHolder != null && newHolder.itemView != null) { + // carry over translation values + endAnimation(newHolder); + ViewCompat.setTranslationX(newHolder.itemView, -deltaX); + ViewCompat.setTranslationY(newHolder.itemView, -deltaY); + ViewCompat.setAlpha(newHolder.itemView, 0); + } + mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY)); + return true; + } + + private void animateChangeImpl(final ChangeInfo changeInfo) { + final ViewHolder holder = changeInfo.oldHolder; + final View view = holder == null ? null : holder.itemView; + final ViewHolder newHolder = changeInfo.newHolder; + final View newView = newHolder != null ? newHolder.itemView : null; + if (view != null) { + final ViewPropertyAnimatorCompat oldViewAnim = ViewCompat.animate(view).setDuration( + getChangeDuration()); + mChangeAnimations.add(changeInfo.oldHolder); + oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX); + oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY); + oldViewAnim.alpha(0).setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchChangeStarting(changeInfo.oldHolder, true); + } + + @Override + public void onAnimationEnd(View view) { + oldViewAnim.setListener(null); + ViewCompat.setAlpha(view, 1); + ViewCompat.setTranslationX(view, 0); + ViewCompat.setTranslationY(view, 0); + dispatchChangeFinished(changeInfo.oldHolder, true); + mChangeAnimations.remove(changeInfo.oldHolder); + dispatchFinishedWhenDone(); + } + }).start(); + } + if (newView != null) { + final ViewPropertyAnimatorCompat newViewAnimation = ViewCompat.animate(newView); + mChangeAnimations.add(changeInfo.newHolder); + newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()). + alpha(1).setListener(new VpaListenerAdapter() { + @Override + public void onAnimationStart(View view) { + dispatchChangeStarting(changeInfo.newHolder, false); + } + @Override + public void onAnimationEnd(View view) { + newViewAnimation.setListener(null); + ViewCompat.setAlpha(newView, 1); + ViewCompat.setTranslationX(newView, 0); + ViewCompat.setTranslationY(newView, 0); + dispatchChangeFinished(changeInfo.newHolder, false); + mChangeAnimations.remove(changeInfo.newHolder); + dispatchFinishedWhenDone(); + } + }).start(); + } + } + + private void endChangeAnimation(List infoList, ViewHolder item) { + for (int i = infoList.size() - 1; i >= 0; i--) { + ChangeInfo changeInfo = infoList.get(i); + if (endChangeAnimationIfNecessary(changeInfo, item)) { + if (changeInfo.oldHolder == null && changeInfo.newHolder == null) { + infoList.remove(changeInfo); + } + } + } + } + + private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) { + if (changeInfo.oldHolder != null) { + endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder); + } + if (changeInfo.newHolder != null) { + endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder); + } + } + private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, ViewHolder item) { + boolean oldItem = false; + if (changeInfo.newHolder == item) { + changeInfo.newHolder = null; + } else if (changeInfo.oldHolder == item) { + changeInfo.oldHolder = null; + oldItem = true; + } else { + return false; + } + ViewCompat.setAlpha(item.itemView, 1); + ViewCompat.setTranslationX(item.itemView, 0); + ViewCompat.setTranslationY(item.itemView, 0); + dispatchChangeFinished(item, oldItem); + return true; + } + + @Override + public void endAnimation(ViewHolder item) { + final View view = item.itemView; + // this will trigger end callback which should set properties to their target values. + ViewCompat.animate(view).cancel(); + // TODO if some other animations are chained to end, how do we cancel them as well? + for (int i = mPendingMoves.size() - 1; i >= 0; i--) { + MoveInfo moveInfo = mPendingMoves.get(i); + if (moveInfo.holder == item) { + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(item); + mPendingMoves.remove(i); + } + } + endChangeAnimation(mPendingChanges, item); + if (mPendingRemovals.remove(item)) { + ViewCompat.setAlpha(view, 1); + dispatchRemoveFinished(item); + } + if (mPendingAdditions.remove(item)) { + ViewCompat.setAlpha(view, 1); + dispatchAddFinished(item); + } + + for (int i = mChangesList.size() - 1; i >= 0; i--) { + ArrayList changes = mChangesList.get(i); + endChangeAnimation(changes, item); + if (changes.isEmpty()) { + mChangesList.remove(i); + } + } + for (int i = mMovesList.size() - 1; i >= 0; i--) { + ArrayList moves = mMovesList.get(i); + for (int j = moves.size() - 1; j >= 0; j--) { + MoveInfo moveInfo = moves.get(j); + if (moveInfo.holder == item) { + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(item); + moves.remove(j); + if (moves.isEmpty()) { + mMovesList.remove(i); + } + break; + } + } + } + for (int i = mAdditionsList.size() - 1; i >= 0; i--) { + ArrayList additions = mAdditionsList.get(i); + if (additions.remove(item)) { + ViewCompat.setAlpha(view, 1); + dispatchAddFinished(item); + if (additions.isEmpty()) { + mAdditionsList.remove(i); + } + } + } + + // animations should be ended by the cancel above. + if (mRemoveAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mRemoveAnimations list"); + } + + if (mAddAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mAddAnimations list"); + } + + if (mChangeAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mChangeAnimations list"); + } + + if (mMoveAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mMoveAnimations list"); + } + dispatchFinishedWhenDone(); + } + + @Override + public boolean isRunning() { + return (!mPendingAdditions.isEmpty() || + !mPendingChanges.isEmpty() || + !mPendingMoves.isEmpty() || + !mPendingRemovals.isEmpty() || + !mMoveAnimations.isEmpty() || + !mRemoveAnimations.isEmpty() || + !mAddAnimations.isEmpty() || + !mChangeAnimations.isEmpty() || + !mMovesList.isEmpty() || + !mAdditionsList.isEmpty() || + !mChangesList.isEmpty()); + } + + /** + * Check the state of currently pending and running animations. If there are none + * pending/running, call {@link #dispatchAnimationsFinished()} to notify any + * listeners. + */ + private void dispatchFinishedWhenDone() { + if (!isRunning()) { + dispatchAnimationsFinished(); + } + } + + @Override + public void endAnimations() { + int count = mPendingMoves.size(); + for (int i = count - 1; i >= 0; i--) { + MoveInfo item = mPendingMoves.get(i); + View view = item.holder.itemView; + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(item.holder); + mPendingMoves.remove(i); + } + count = mPendingRemovals.size(); + for (int i = count - 1; i >= 0; i--) { + ViewHolder item = mPendingRemovals.get(i); + dispatchRemoveFinished(item); + mPendingRemovals.remove(i); + } + count = mPendingAdditions.size(); + for (int i = count - 1; i >= 0; i--) { + ViewHolder item = mPendingAdditions.get(i); + View view = item.itemView; + ViewCompat.setAlpha(view, 1); + dispatchAddFinished(item); + mPendingAdditions.remove(i); + } + count = mPendingChanges.size(); + for (int i = count - 1; i >= 0; i--) { + endChangeAnimationIfNecessary(mPendingChanges.get(i)); + } + mPendingChanges.clear(); + if (!isRunning()) { + return; + } + + int listCount = mMovesList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList moves = mMovesList.get(i); + count = moves.size(); + for (int j = count - 1; j >= 0; j--) { + MoveInfo moveInfo = moves.get(j); + ViewHolder item = moveInfo.holder; + View view = item.itemView; + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(moveInfo.holder); + moves.remove(j); + if (moves.isEmpty()) { + mMovesList.remove(moves); + } + } + } + listCount = mAdditionsList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList additions = mAdditionsList.get(i); + count = additions.size(); + for (int j = count - 1; j >= 0; j--) { + ViewHolder item = additions.get(j); + View view = item.itemView; + ViewCompat.setAlpha(view, 1); + dispatchAddFinished(item); + additions.remove(j); + if (additions.isEmpty()) { + mAdditionsList.remove(additions); + } + } + } + listCount = mChangesList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList changes = mChangesList.get(i); + count = changes.size(); + for (int j = count - 1; j >= 0; j--) { + endChangeAnimationIfNecessary(changes.get(j)); + if (changes.isEmpty()) { + mChangesList.remove(changes); + } + } + } + + cancelAll(mRemoveAnimations); + cancelAll(mMoveAnimations); + cancelAll(mAddAnimations); + cancelAll(mChangeAnimations); + + dispatchAnimationsFinished(); + } + + void cancelAll(List viewHolders) { + for (int i = viewHolders.size() - 1; i >= 0; i--) { + ViewCompat.animate(viewHolders.get(i).itemView).cancel(); + } + } + + private static class VpaListenerAdapter implements ViewPropertyAnimatorListener { + @Override + public void onAnimationStart(View view) {} + + @Override + public void onAnimationEnd(View view) {} + + @Override + public void onAnimationCancel(View view) {} + }; +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/GridLayoutManager.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/GridLayoutManager.java new file mode 100644 index 000000000..790bf06f8 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/GridLayoutManager.java @@ -0,0 +1,887 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific languag`e governing permissions and + * limitations under the License. + */ +package org.telegram.android.support.widget; + +import android.content.Context; +import android.graphics.Rect; +import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; +import android.util.AttributeSet; +import android.util.Log; +import android.util.SparseIntArray; +import android.view.View; +import android.view.ViewGroup; + +import java.util.Arrays; + +/** + * A {@link RecyclerView.LayoutManager} implementations that lays out items in a grid. + *

+ * By default, each item occupies 1 span. You can change it by providing a custom + * {@link SpanSizeLookup} instance via {@link #setSpanSizeLookup(SpanSizeLookup)}. + */ +public class GridLayoutManager extends LinearLayoutManager { + + private static final boolean DEBUG = false; + private static final String TAG = "GridLayoutManager"; + public static final int DEFAULT_SPAN_COUNT = -1; + /** + * The measure spec for the scroll direction. + */ + static final int MAIN_DIR_SPEC = + View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); + /** + * Span size have been changed but we've not done a new layout calculation. + */ + boolean mPendingSpanCountChange = false; + int mSpanCount = DEFAULT_SPAN_COUNT; + /** + * Right borders for each span. + *

For i-th item start is {@link #mCachedBorders}[i-1] + 1 + * and end is {@link #mCachedBorders}[i]. + */ + int [] mCachedBorders; + /** + * Temporary array to keep views in layoutChunk method + */ + View[] mSet; + final SparseIntArray mPreLayoutSpanSizeCache = new SparseIntArray(); + final SparseIntArray mPreLayoutSpanIndexCache = new SparseIntArray(); + SpanSizeLookup mSpanSizeLookup = new DefaultSpanSizeLookup(); + // re-used variable to acquire decor insets from RecyclerView + final Rect mDecorInsets = new Rect(); + + /** + * Creates a vertical GridLayoutManager + * + * @param context Current context, will be used to access resources. + * @param spanCount The number of columns in the grid + */ + public GridLayoutManager(Context context, int spanCount) { + super(context); + setSpanCount(spanCount); + } + + /** + * @param context Current context, will be used to access resources. + * @param spanCount The number of columns or rows in the grid + * @param orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link + * #VERTICAL}. + * @param reverseLayout When set to true, layouts from end to start. + */ + public GridLayoutManager(Context context, int spanCount, int orientation, + boolean reverseLayout) { + super(context, orientation, reverseLayout); + setSpanCount(spanCount); + } + + /** + * stackFromEnd is not supported by GridLayoutManager. Consider using + * {@link #setReverseLayout(boolean)}. + */ + @Override + public void setStackFromEnd(boolean stackFromEnd) { + if (stackFromEnd) { + throw new UnsupportedOperationException( + "GridLayoutManager does not support stack from end." + + " Consider using reverse layout"); + } + super.setStackFromEnd(false); + } + + @Override + public int getRowCountForAccessibility(RecyclerView.Recycler recycler, + RecyclerView.State state) { + if (mOrientation == HORIZONTAL) { + return mSpanCount; + } + if (state.getItemCount() < 1) { + return 0; + } + return getSpanGroupIndex(recycler, state, state.getItemCount() - 1); + } + + @Override + public int getColumnCountForAccessibility(RecyclerView.Recycler recycler, + RecyclerView.State state) { + if (mOrientation == VERTICAL) { + return mSpanCount; + } + if (state.getItemCount() < 1) { + return 0; + } + return getSpanGroupIndex(recycler, state, state.getItemCount() - 1); + } + + @Override + public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler, + RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) { + ViewGroup.LayoutParams lp = host.getLayoutParams(); + if (!(lp instanceof LayoutParams)) { + super.onInitializeAccessibilityNodeInfoForItem(host, info); + return; + } + LayoutParams glp = (LayoutParams) lp; + int spanGroupIndex = getSpanGroupIndex(recycler, state, glp.getViewLayoutPosition()); + if (mOrientation == HORIZONTAL) { + info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain( + glp.getSpanIndex(), glp.getSpanSize(), + spanGroupIndex, 1, + mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false)); + } else { // VERTICAL + info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain( + spanGroupIndex , 1, + glp.getSpanIndex(), glp.getSpanSize(), + mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false)); + } + } + + @Override + public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { + if (state.isPreLayout()) { + cachePreLayoutSpanMapping(); + } + super.onLayoutChildren(recycler, state); + if (DEBUG) { + validateChildOrder(); + } + clearPreLayoutSpanMappingCache(); + if (!state.isPreLayout()) { + mPendingSpanCountChange = false; + } + } + + private void clearPreLayoutSpanMappingCache() { + mPreLayoutSpanSizeCache.clear(); + mPreLayoutSpanIndexCache.clear(); + } + + private void cachePreLayoutSpanMapping() { + final int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + final LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams(); + final int viewPosition = lp.getViewLayoutPosition(); + mPreLayoutSpanSizeCache.put(viewPosition, lp.getSpanSize()); + mPreLayoutSpanIndexCache.put(viewPosition, lp.getSpanIndex()); + } + } + + @Override + public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) { + mSpanSizeLookup.invalidateSpanIndexCache(); + } + + @Override + public void onItemsChanged(RecyclerView recyclerView) { + mSpanSizeLookup.invalidateSpanIndexCache(); + } + + @Override + public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) { + mSpanSizeLookup.invalidateSpanIndexCache(); + } + + @Override + public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) { + mSpanSizeLookup.invalidateSpanIndexCache(); + } + + @Override + public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) { + mSpanSizeLookup.invalidateSpanIndexCache(); + } + + @Override + public RecyclerView.LayoutParams generateDefaultLayoutParams() { + return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT); + } + + @Override + public RecyclerView.LayoutParams generateLayoutParams(Context c, AttributeSet attrs) { + return new LayoutParams(c, attrs); + } + + @Override + public RecyclerView.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { + if (lp instanceof ViewGroup.MarginLayoutParams) { + return new LayoutParams((ViewGroup.MarginLayoutParams) lp); + } else { + return new LayoutParams(lp); + } + } + + @Override + public boolean checkLayoutParams(RecyclerView.LayoutParams lp) { + return lp instanceof LayoutParams; + } + + /** + * Sets the source to get the number of spans occupied by each item in the adapter. + * + * @param spanSizeLookup {@link SpanSizeLookup} instance to be used to query number of spans + * occupied by each item + */ + public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup) { + mSpanSizeLookup = spanSizeLookup; + } + + /** + * Returns the current {@link SpanSizeLookup} used by the GridLayoutManager. + * + * @return The current {@link SpanSizeLookup} used by the GridLayoutManager. + */ + public SpanSizeLookup getSpanSizeLookup() { + return mSpanSizeLookup; + } + + private void updateMeasurements() { + int totalSpace; + if (getOrientation() == VERTICAL) { + totalSpace = getWidth() - getPaddingRight() - getPaddingLeft(); + } else { + totalSpace = getHeight() - getPaddingBottom() - getPaddingTop(); + } + calculateItemBorders(totalSpace); + } + + private void calculateItemBorders(int totalSpace) { + if (mCachedBorders == null || mCachedBorders.length != mSpanCount + 1 + || mCachedBorders[mCachedBorders.length - 1] != totalSpace) { + mCachedBorders = new int[mSpanCount + 1]; + } + mCachedBorders[0] = 0; + int sizePerSpan = totalSpace / mSpanCount; + int sizePerSpanRemainder = totalSpace % mSpanCount; + int consumedPixels = 0; + int additionalSize = 0; + for (int i = 1; i <= mSpanCount; i++) { + int itemSize = sizePerSpan; + additionalSize += sizePerSpanRemainder; + if (additionalSize > 0 && (mSpanCount - additionalSize) < sizePerSpanRemainder) { + itemSize += 1; + additionalSize -= mSpanCount; + } + consumedPixels += itemSize; + mCachedBorders[i] = consumedPixels; + } + } + + @Override + void onAnchorReady(RecyclerView.State state, AnchorInfo anchorInfo) { + super.onAnchorReady(state, anchorInfo); + updateMeasurements(); + if (state.getItemCount() > 0 && !state.isPreLayout()) { + ensureAnchorIsInFirstSpan(anchorInfo); + } + if (mSet == null || mSet.length != mSpanCount) { + mSet = new View[mSpanCount]; + } + } + + private void ensureAnchorIsInFirstSpan(AnchorInfo anchorInfo) { + int span = mSpanSizeLookup.getCachedSpanIndex(anchorInfo.mPosition, mSpanCount); + while (span > 0 && anchorInfo.mPosition > 0) { + anchorInfo.mPosition--; + span = mSpanSizeLookup.getCachedSpanIndex(anchorInfo.mPosition, mSpanCount); + } + } + + @Override + View findReferenceChild(int start, int end, int itemCount) { + ensureLayoutState(); + View invalidMatch = null; + View outOfBoundsMatch = null; + final int boundsStart = mOrientationHelper.getStartAfterPadding(); + final int boundsEnd = mOrientationHelper.getEndAfterPadding(); + final int diff = end > start ? 1 : -1; + for (int i = start; i != end; i += diff) { + final View view = getChildAt(i); + final int position = getPosition(view); + if (position >= 0 && position < itemCount) { + final int span = mSpanSizeLookup.getCachedSpanIndex(position, mSpanCount); + if (span != 0) { + continue; + } + if (((RecyclerView.LayoutParams) view.getLayoutParams()).isItemRemoved()) { + if (invalidMatch == null) { + invalidMatch = view; // removed item, least preferred + } + } else if (mOrientationHelper.getDecoratedStart(view) >= boundsEnd || + mOrientationHelper.getDecoratedEnd(view) < boundsStart) { + if (outOfBoundsMatch == null) { + outOfBoundsMatch = view; // item is not visible, less preferred + } + } else { + return view; + } + } + } + return outOfBoundsMatch != null ? outOfBoundsMatch : invalidMatch; + } + + private int getSpanGroupIndex(RecyclerView.Recycler recycler, RecyclerView.State state, + int viewPosition) { + if (!state.isPreLayout()) { + return mSpanSizeLookup.getSpanGroupIndex(viewPosition, mSpanCount); + } + final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(viewPosition); + if (adapterPosition == -1) { + if (DEBUG) { + throw new RuntimeException("Cannot find span group index for position " + + viewPosition); + } + Log.w(TAG, "Cannot find span size for pre layout position. " + viewPosition); + return 0; + } + return mSpanSizeLookup.getSpanGroupIndex(adapterPosition, mSpanCount); + } + + private int getSpanIndex(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) { + if (!state.isPreLayout()) { + return mSpanSizeLookup.getCachedSpanIndex(pos, mSpanCount); + } + final int cached = mPreLayoutSpanIndexCache.get(pos, -1); + if (cached != -1) { + return cached; + } + final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos); + if (adapterPosition == -1) { + if (DEBUG) { + throw new RuntimeException("Cannot find span index for pre layout position. It is" + + " not cached, not in the adapter. Pos:" + pos); + } + Log.w(TAG, "Cannot find span size for pre layout position. It is" + + " not cached, not in the adapter. Pos:" + pos); + return 0; + } + return mSpanSizeLookup.getCachedSpanIndex(adapterPosition, mSpanCount); + } + + private int getSpanSize(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) { + if (!state.isPreLayout()) { + return mSpanSizeLookup.getSpanSize(pos); + } + final int cached = mPreLayoutSpanSizeCache.get(pos, -1); + if (cached != -1) { + return cached; + } + final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos); + if (adapterPosition == -1) { + if (DEBUG) { + throw new RuntimeException("Cannot find span size for pre layout position. It is" + + " not cached, not in the adapter. Pos:" + pos); + } + Log.w(TAG, "Cannot find span size for pre layout position. It is" + + " not cached, not in the adapter. Pos:" + pos); + return 1; + } + return mSpanSizeLookup.getSpanSize(adapterPosition); + } + + @Override + void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, + LayoutState layoutState, LayoutChunkResult result) { + final boolean layingOutInPrimaryDirection = + layoutState.mItemDirection == LayoutState.ITEM_DIRECTION_TAIL; + int count = 0; + int consumedSpanCount = 0; + int remainingSpan = mSpanCount; + if (!layingOutInPrimaryDirection) { + int itemSpanIndex = getSpanIndex(recycler, state, layoutState.mCurrentPosition); + int itemSpanSize = getSpanSize(recycler, state, layoutState.mCurrentPosition); + remainingSpan = itemSpanIndex + itemSpanSize; + } + while (count < mSpanCount && layoutState.hasMore(state) && remainingSpan > 0) { + int pos = layoutState.mCurrentPosition; + final int spanSize = getSpanSize(recycler, state, pos); + if (spanSize > mSpanCount) { + throw new IllegalArgumentException("Item at position " + pos + " requires " + + spanSize + " spans but GridLayoutManager has only " + mSpanCount + + " spans."); + } + remainingSpan -= spanSize; + if (remainingSpan < 0) { + break; // item did not fit into this row or column + } + View view = layoutState.next(recycler); + if (view == null) { + break; + } + consumedSpanCount += spanSize; + mSet[count] = view; + count++; + } + + if (count == 0) { + result.mFinished = true; + return; + } + + int maxSize = 0; + + // we should assign spans before item decor offsets are calculated + assignSpans(recycler, state, count, consumedSpanCount, layingOutInPrimaryDirection); + for (int i = 0; i < count; i++) { + View view = mSet[i]; + if (layoutState.mScrapList == null) { + if (layingOutInPrimaryDirection) { + addView(view); + } else { + addView(view, 0); + } + } else { + if (layingOutInPrimaryDirection) { + addDisappearingView(view); + } else { + addDisappearingView(view, 0); + } + } + + final LayoutParams lp = (LayoutParams) view.getLayoutParams(); + final int spec = View.MeasureSpec.makeMeasureSpec( + mCachedBorders[lp.mSpanIndex + lp.mSpanSize] - + mCachedBorders[lp.mSpanIndex], + View.MeasureSpec.EXACTLY); + if (mOrientation == VERTICAL) { + measureChildWithDecorationsAndMargin(view, spec, getMainDirSpec(lp.height)); + } else { + measureChildWithDecorationsAndMargin(view, getMainDirSpec(lp.width), spec); + } + final int size = mOrientationHelper.getDecoratedMeasurement(view); + if (size > maxSize) { + maxSize = size; + } + } + + // views that did not measure the maxSize has to be re-measured + final int maxMeasureSpec = getMainDirSpec(maxSize); + for (int i = 0; i < count; i ++) { + final View view = mSet[i]; + if (mOrientationHelper.getDecoratedMeasurement(view) != maxSize) { + final LayoutParams lp = (LayoutParams) view.getLayoutParams(); + final int spec = View.MeasureSpec.makeMeasureSpec( + mCachedBorders[lp.mSpanIndex + lp.mSpanSize] - + mCachedBorders[lp.mSpanIndex], + View.MeasureSpec.EXACTLY); + if (mOrientation == VERTICAL) { + measureChildWithDecorationsAndMargin(view, spec, maxMeasureSpec); + } else { + measureChildWithDecorationsAndMargin(view, maxMeasureSpec, spec); + } + } + } + + result.mConsumed = maxSize; + + int left = 0, right = 0, top = 0, bottom = 0; + if (mOrientation == VERTICAL) { + if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { + bottom = layoutState.mOffset; + top = bottom - maxSize; + } else { + top = layoutState.mOffset; + bottom = top + maxSize; + } + } else { + if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { + right = layoutState.mOffset; + left = right - maxSize; + } else { + left = layoutState.mOffset; + right = left + maxSize; + } + } + for (int i = 0; i < count; i++) { + View view = mSet[i]; + LayoutParams params = (LayoutParams) view.getLayoutParams(); + if (mOrientation == VERTICAL) { + left = getPaddingLeft() + mCachedBorders[params.mSpanIndex]; + right = left + mOrientationHelper.getDecoratedMeasurementInOther(view); + } else { + top = getPaddingTop() + mCachedBorders[params.mSpanIndex]; + bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view); + } + // We calculate everything with View's bounding box (which includes decor and margins) + // To calculate correct layout position, we subtract margins. + layoutDecorated(view, left + params.leftMargin, top + params.topMargin, + right - params.rightMargin, bottom - params.bottomMargin); + if (DEBUG) { + Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:" + + (left + params.leftMargin) + ", t:" + (top + params.topMargin) + ", r:" + + (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin) + + ", span:" + params.mSpanIndex + ", spanSize:" + params.mSpanSize); + } + // Consume the available space if the view is not removed OR changed + if (params.isItemRemoved() || params.isItemChanged()) { + result.mIgnoreConsumed = true; + } + result.mFocusable |= view.isFocusable(); + } + Arrays.fill(mSet, null); + } + + private int getMainDirSpec(int dim) { + if (dim < 0) { + return MAIN_DIR_SPEC; + } else { + return View.MeasureSpec.makeMeasureSpec(dim, View.MeasureSpec.EXACTLY); + } + } + + private void measureChildWithDecorationsAndMargin(View child, int widthSpec, int heightSpec) { + calculateItemDecorationsForChild(child, mDecorInsets); + RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams(); + widthSpec = updateSpecWithExtra(widthSpec, lp.leftMargin + mDecorInsets.left, + lp.rightMargin + mDecorInsets.right); + heightSpec = updateSpecWithExtra(heightSpec, lp.topMargin + mDecorInsets.top, + lp.bottomMargin + mDecorInsets.bottom); + child.measure(widthSpec, heightSpec); + } + + private int updateSpecWithExtra(int spec, int startInset, int endInset) { + if (startInset == 0 && endInset == 0) { + return spec; + } + final int mode = View.MeasureSpec.getMode(spec); + if (mode == View.MeasureSpec.AT_MOST || mode == View.MeasureSpec.EXACTLY) { + return View.MeasureSpec.makeMeasureSpec( + View.MeasureSpec.getSize(spec) - startInset - endInset, mode); + } + return spec; + } + + private void assignSpans(RecyclerView.Recycler recycler, RecyclerView.State state, int count, + int consumedSpanCount, boolean layingOutInPrimaryDirection) { + int span, spanDiff, start, end, diff; + // make sure we traverse from min position to max position + if (layingOutInPrimaryDirection) { + start = 0; + end = count; + diff = 1; + } else { + start = count - 1; + end = -1; + diff = -1; + } + if (mOrientation == VERTICAL && isLayoutRTL()) { // start from last span + span = mSpanCount - 1; + spanDiff = -1; + } else { + span = 0; + spanDiff = 1; + } + for (int i = start; i != end; i += diff) { + View view = mSet[i]; + LayoutParams params = (LayoutParams) view.getLayoutParams(); + params.mSpanSize = getSpanSize(recycler, state, getPosition(view)); + if (spanDiff == -1 && params.mSpanSize > 1) { + params.mSpanIndex = span - (params.mSpanSize - 1); + } else { + params.mSpanIndex = span; + } + span += spanDiff * params.mSpanSize; + } + } + + /** + * Returns the number of spans laid out by this grid. + * + * @return The number of spans + * @see #setSpanCount(int) + */ + public int getSpanCount() { + return mSpanCount; + } + + /** + * Sets the number of spans to be laid out. + *

+ * If {@link #getOrientation()} is {@link #VERTICAL}, this is the number of columns. + * If {@link #getOrientation()} is {@link #HORIZONTAL}, this is the number of rows. + * + * @param spanCount The total number of spans in the grid + * @see #getSpanCount() + */ + public void setSpanCount(int spanCount) { + if (spanCount == mSpanCount) { + return; + } + mPendingSpanCountChange = true; + if (spanCount < 1) { + throw new IllegalArgumentException("Span count should be at least 1. Provided " + + spanCount); + } + mSpanCount = spanCount; + mSpanSizeLookup.invalidateSpanIndexCache(); + } + + /** + * A helper class to provide the number of spans each item occupies. + *

+ * Default implementation sets each item to occupy exactly 1 span. + * + * @see GridLayoutManager#setSpanSizeLookup(SpanSizeLookup) + */ + public static abstract class SpanSizeLookup { + + final SparseIntArray mSpanIndexCache = new SparseIntArray(); + + private boolean mCacheSpanIndices = false; + + /** + * Returns the number of span occupied by the item at position. + * + * @param position The adapter position of the item + * @return The number of spans occupied by the item at the provided position + */ + abstract public int getSpanSize(int position); + + /** + * Sets whether the results of {@link #getSpanIndex(int, int)} method should be cached or + * not. By default these values are not cached. If you are not overriding + * {@link #getSpanIndex(int, int)}, you should set this to true for better performance. + * + * @param cacheSpanIndices Whether results of getSpanIndex should be cached or not. + */ + public void setSpanIndexCacheEnabled(boolean cacheSpanIndices) { + mCacheSpanIndices = cacheSpanIndices; + } + + /** + * Clears the span index cache. GridLayoutManager automatically calls this method when + * adapter changes occur. + */ + public void invalidateSpanIndexCache() { + mSpanIndexCache.clear(); + } + + /** + * Returns whether results of {@link #getSpanIndex(int, int)} method are cached or not. + * + * @return True if results of {@link #getSpanIndex(int, int)} are cached. + */ + public boolean isSpanIndexCacheEnabled() { + return mCacheSpanIndices; + } + + int getCachedSpanIndex(int position, int spanCount) { + if (!mCacheSpanIndices) { + return getSpanIndex(position, spanCount); + } + final int existing = mSpanIndexCache.get(position, -1); + if (existing != -1) { + return existing; + } + final int value = getSpanIndex(position, spanCount); + mSpanIndexCache.put(position, value); + return value; + } + + /** + * Returns the final span index of the provided position. + *

+ * If you have a faster way to calculate span index for your items, you should override + * this method. Otherwise, you should enable span index cache + * ({@link #setSpanIndexCacheEnabled(boolean)}) for better performance. When caching is + * disabled, default implementation traverses all items from 0 to + * position. When caching is enabled, it calculates from the closest cached + * value before the position. + *

+ * If you override this method, you need to make sure it is consistent with + * {@link #getSpanSize(int)}. GridLayoutManager does not call this method for + * each item. It is called only for the reference item and rest of the items + * are assigned to spans based on the reference item. For example, you cannot assign a + * position to span 2 while span 1 is empty. + *

+ * Note that span offsets always start with 0 and are not affected by RTL. + * + * @param position The position of the item + * @param spanCount The total number of spans in the grid + * @return The final span position of the item. Should be between 0 (inclusive) and + * spanCount(exclusive) + */ + public int getSpanIndex(int position, int spanCount) { + int positionSpanSize = getSpanSize(position); + if (positionSpanSize == spanCount) { + return 0; // quick return for full-span items + } + int span = 0; + int startPos = 0; + // If caching is enabled, try to jump + if (mCacheSpanIndices && mSpanIndexCache.size() > 0) { + int prevKey = findReferenceIndexFromCache(position); + if (prevKey >= 0) { + span = mSpanIndexCache.get(prevKey) + getSpanSize(prevKey); + startPos = prevKey + 1; + } + } + for (int i = startPos; i < position; i++) { + int size = getSpanSize(i); + span += size; + if (span == spanCount) { + span = 0; + } else if (span > spanCount) { + // did not fit, moving to next row / column + span = size; + } + } + if (span + positionSpanSize <= spanCount) { + return span; + } + return 0; + } + + int findReferenceIndexFromCache(int position) { + int lo = 0; + int hi = mSpanIndexCache.size() - 1; + + while (lo <= hi) { + final int mid = (lo + hi) >>> 1; + final int midVal = mSpanIndexCache.keyAt(mid); + if (midVal < position) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + int index = lo - 1; + if (index >= 0 && index < mSpanIndexCache.size()) { + return mSpanIndexCache.keyAt(index); + } + return -1; + } + + /** + * Returns the index of the group this position belongs. + *

+ * For example, if grid has 3 columns and each item occupies 1 span, span group index + * for item 1 will be 0, item 5 will be 1. + * + * @param adapterPosition The position in adapter + * @param spanCount The total number of spans in the grid + * @return The index of the span group including the item at the given adapter position + */ + public int getSpanGroupIndex(int adapterPosition, int spanCount) { + int span = 0; + int group = 0; + int positionSpanSize = getSpanSize(adapterPosition); + for (int i = 0; i < adapterPosition; i++) { + int size = getSpanSize(i); + span += size; + if (span == spanCount) { + span = 0; + group++; + } else if (span > spanCount) { + // did not fit, moving to next row / column + span = size; + group++; + } + } + if (span + positionSpanSize > spanCount) { + group++; + } + return group; + } + } + + @Override + public boolean supportsPredictiveItemAnimations() { + return mPendingSavedState == null && !mPendingSpanCountChange; + } + + /** + * Default implementation for {@link SpanSizeLookup}. Each item occupies 1 span. + */ + public static final class DefaultSpanSizeLookup extends SpanSizeLookup { + + @Override + public int getSpanSize(int position) { + return 1; + } + + @Override + public int getSpanIndex(int position, int spanCount) { + return position % spanCount; + } + } + + /** + * LayoutParams used by GridLayoutManager. + *

+ * Note that if the orientation is {@link #VERTICAL}, the width parameter is ignored and if the + * orientation is {@link #HORIZONTAL} the height parameter is ignored because child view is + * expected to fill all of the space given to it. + */ + public static class LayoutParams extends RecyclerView.LayoutParams { + + /** + * Span Id for Views that are not laid out yet. + */ + public static final int INVALID_SPAN_ID = -1; + + private int mSpanIndex = INVALID_SPAN_ID; + + private int mSpanSize = 0; + + public LayoutParams(Context c, AttributeSet attrs) { + super(c, attrs); + } + + public LayoutParams(int width, int height) { + super(width, height); + } + + public LayoutParams(ViewGroup.MarginLayoutParams source) { + super(source); + } + + public LayoutParams(ViewGroup.LayoutParams source) { + super(source); + } + + public LayoutParams(RecyclerView.LayoutParams source) { + super(source); + } + + /** + * Returns the current span index of this View. If the View is not laid out yet, the return + * value is undefined. + *

+ * Note that span index may change by whether the RecyclerView is RTL or not. For + * example, if the number of spans is 3 and layout is RTL, the rightmost item will have + * span index of 2. If the layout changes back to LTR, span index for this view will be 0. + * If the item was occupying 2 spans, span indices would be 1 and 0 respectively. + *

+ * If the View occupies multiple spans, span with the minimum index is returned. + * + * @return The span index of the View. + */ + public int getSpanIndex() { + return mSpanIndex; + } + + /** + * Returns the number of spans occupied by this View. If the View not laid out yet, the + * return value is undefined. + * + * @return The number of spans occupied by this View. + */ + public int getSpanSize() { + return mSpanSize; + } + } + +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/LayoutState.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/LayoutState.java new file mode 100644 index 000000000..c431b6149 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/LayoutState.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific languag`e governing permissions and + * limitations under the License. + */ + +package org.telegram.android.support.widget; +import android.view.View; + +/** + * Helper class that keeps temporary state while {LayoutManager} is filling out the empty + * space. + */ +class LayoutState { + + final static String TAG = "LayoutState"; + + final static int LAYOUT_START = -1; + + final static int LAYOUT_END = 1; + + final static int INVALID_LAYOUT = Integer.MIN_VALUE; + + final static int ITEM_DIRECTION_HEAD = -1; + + final static int ITEM_DIRECTION_TAIL = 1; + + final static int SCOLLING_OFFSET_NaN = Integer.MIN_VALUE; + + /** + * Number of pixels that we should fill, in the layout direction. + */ + int mAvailable; + + /** + * Current position on the adapter to get the next item. + */ + int mCurrentPosition; + + /** + * Defines the direction in which the data adapter is traversed. + * Should be {@link #ITEM_DIRECTION_HEAD} or {@link #ITEM_DIRECTION_TAIL} + */ + int mItemDirection; + + /** + * Defines the direction in which the layout is filled. + * Should be {@link #LAYOUT_START} or {@link #LAYOUT_END} + */ + int mLayoutDirection; + + /** + * Used if you want to pre-layout items that are not yet visible. + * The difference with {@link #mAvailable} is that, when recycling, distance rendered for + * {@link #mExtra} is not considered not to recycle visible children. + */ + int mExtra = 0; + + /** + * @return true if there are more items in the data adapter + */ + boolean hasMore(RecyclerView.State state) { + return mCurrentPosition >= 0 && mCurrentPosition < state.getItemCount(); + } + + /** + * Gets the view for the next element that we should render. + * Also updates current item index to the next item, based on {@link #mItemDirection} + * + * @return The next element that we should render. + */ + View next(RecyclerView.Recycler recycler) { + final View view = recycler.getViewForPosition(mCurrentPosition); + mCurrentPosition += mItemDirection; + return view; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/LinearLayoutManager.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/LinearLayoutManager.java new file mode 100644 index 000000000..a52bbffe5 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/LinearLayoutManager.java @@ -0,0 +1,2131 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific languag`e governing permissions and + * limitations under the License. + */ + +package org.telegram.android.support.widget; + +import android.content.Context; +import android.graphics.PointF; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.v4.view.ViewCompat; +import android.support.v4.view.accessibility.AccessibilityEventCompat; +import android.support.v4.view.accessibility.AccessibilityRecordCompat; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; + +import java.util.List; + +import static org.telegram.android.support.widget.RecyclerView.NO_POSITION; + +/** + * A {@link android.support.v7.widget.RecyclerView.LayoutManager} implementation which provides + * similar functionality to {@link android.widget.ListView}. + */ +public class LinearLayoutManager extends RecyclerView.LayoutManager { + + private static final String TAG = "LinearLayoutManager"; + + private static final boolean DEBUG = false; + + public static final int HORIZONTAL = OrientationHelper.HORIZONTAL; + + public static final int VERTICAL = OrientationHelper.VERTICAL; + + public static final int INVALID_OFFSET = Integer.MIN_VALUE; + + + /** + * While trying to find next view to focus, LayoutManager will not try to scroll more + * than this factor times the total space of the list. If layout is vertical, total space is the + * height minus padding, if layout is horizontal, total space is the width minus padding. + */ + private static final float MAX_SCROLL_FACTOR = 0.33f; + + + /** + * Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL} + */ + int mOrientation; + + /** + * Helper class that keeps temporary layout state. + * It does not keep state after layout is complete but we still keep a reference to re-use + * the same object. + */ + private LayoutState mLayoutState; + + /** + * Many calculations are made depending on orientation. To keep it clean, this interface + * helps {@link LinearLayoutManager} make those decisions. + * Based on {@link #mOrientation}, an implementation is lazily created in + * {@link #ensureLayoutState} method. + */ + OrientationHelper mOrientationHelper; + + /** + * We need to track this so that we can ignore current position when it changes. + */ + private boolean mLastStackFromEnd; + + + /** + * Defines if layout should be calculated from end to start. + * + * @see #mShouldReverseLayout + */ + private boolean mReverseLayout = false; + + /** + * This keeps the final value for how LayoutManager should start laying out views. + * It is calculated by checking {@link #getReverseLayout()} and View's layout direction. + * {@link #onLayoutChildren(RecyclerView.Recycler, RecyclerView.State)} is run. + */ + boolean mShouldReverseLayout = false; + + /** + * Works the same way as {@link android.widget.AbsListView#setStackFromBottom(boolean)} and + * it supports both orientations. + * see {@link android.widget.AbsListView#setStackFromBottom(boolean)} + */ + private boolean mStackFromEnd = false; + + /** + * Works the same way as {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)}. + * see {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)} + */ + private boolean mSmoothScrollbarEnabled = true; + + /** + * When LayoutManager needs to scroll to a position, it sets this variable and requests a + * layout which will check this variable and re-layout accordingly. + */ + int mPendingScrollPosition = NO_POSITION; + + /** + * Used to keep the offset value when {@link #scrollToPositionWithOffset(int, int)} is + * called. + */ + int mPendingScrollPositionOffset = INVALID_OFFSET; + + private boolean mRecycleChildrenOnDetach; + + SavedState mPendingSavedState = null; + + /** + * Re-used variable to keep anchor information on re-layout. + * Anchor position and coordinate defines the reference point for LLM while doing a layout. + * */ + final AnchorInfo mAnchorInfo; + + /** + * Creates a vertical LinearLayoutManager + * + * @param context Current context, will be used to access resources. + */ + public LinearLayoutManager(Context context) { + this(context, VERTICAL, false); + } + + /** + * @param context Current context, will be used to access resources. + * @param orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link + * #VERTICAL}. + * @param reverseLayout When set to true, layouts from end to start. + */ + public LinearLayoutManager(Context context, int orientation, boolean reverseLayout) { + mAnchorInfo = new AnchorInfo(); + setOrientation(orientation); + setReverseLayout(reverseLayout); + } + + /** + * {@inheritDoc} + */ + @Override + public RecyclerView.LayoutParams generateDefaultLayoutParams() { + return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT); + } + + /** + * Returns whether LayoutManager will recycle its children when it is detached from + * RecyclerView. + * + * @return true if LayoutManager will recycle its children when it is detached from + * RecyclerView. + */ + public boolean getRecycleChildrenOnDetach() { + return mRecycleChildrenOnDetach; + } + + /** + * Set whether LayoutManager will recycle its children when it is detached from + * RecyclerView. + *

+ * If you are using a {@link RecyclerView.RecycledViewPool}, it might be a good idea to set + * this flag to true so that views will be avilable to other RecyclerViews + * immediately. + *

+ * Note that, setting this flag will result in a performance drop if RecyclerView + * is restored. + * + * @param recycleChildrenOnDetach Whether children should be recycled in detach or not. + */ + public void setRecycleChildrenOnDetach(boolean recycleChildrenOnDetach) { + mRecycleChildrenOnDetach = recycleChildrenOnDetach; + } + + @Override + public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) { + super.onDetachedFromWindow(view, recycler); + if (mRecycleChildrenOnDetach) { + removeAndRecycleAllViews(recycler); + recycler.clear(); + } + } + + @Override + public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + super.onInitializeAccessibilityEvent(event); + if (getChildCount() > 0) { + final AccessibilityRecordCompat record = AccessibilityEventCompat + .asRecord(event); + record.setFromIndex(findFirstVisibleItemPosition()); + record.setToIndex(findLastVisibleItemPosition()); + } + } + + @Override + public Parcelable onSaveInstanceState() { + if (mPendingSavedState != null) { + return new SavedState(mPendingSavedState); + } + SavedState state = new SavedState(); + if (getChildCount() > 0) { + ensureLayoutState(); + boolean didLayoutFromEnd = mLastStackFromEnd ^ mShouldReverseLayout; + state.mAnchorLayoutFromEnd = didLayoutFromEnd; + if (didLayoutFromEnd) { + final View refChild = getChildClosestToEnd(); + state.mAnchorOffset = mOrientationHelper.getEndAfterPadding() - + mOrientationHelper.getDecoratedEnd(refChild); + state.mAnchorPosition = getPosition(refChild); + } else { + final View refChild = getChildClosestToStart(); + state.mAnchorPosition = getPosition(refChild); + state.mAnchorOffset = mOrientationHelper.getDecoratedStart(refChild) - + mOrientationHelper.getStartAfterPadding(); + } + } else { + state.invalidateAnchor(); + } + return state; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + if (state instanceof SavedState) { + mPendingSavedState = (SavedState) state; + requestLayout(); + if (DEBUG) { + Log.d(TAG, "loaded saved state"); + } + } else if (DEBUG) { + Log.d(TAG, "invalid saved state class"); + } + } + + /** + * @return true if {@link #getOrientation()} is {@link #HORIZONTAL} + */ + @Override + public boolean canScrollHorizontally() { + return mOrientation == HORIZONTAL; + } + + /** + * @return true if {@link #getOrientation()} is {@link #VERTICAL} + */ + @Override + public boolean canScrollVertically() { + return mOrientation == VERTICAL; + } + + /** + * Compatibility support for {@link android.widget.AbsListView#setStackFromBottom(boolean)} + */ + public void setStackFromEnd(boolean stackFromEnd) { + assertNotInLayoutOrScroll(null); + if (mStackFromEnd == stackFromEnd) { + return; + } + mStackFromEnd = stackFromEnd; + requestLayout(); + } + + public boolean getStackFromEnd() { + return mStackFromEnd; + } + + /** + * Returns the current orientaion of the layout. + * + * @return Current orientation. + * @see #mOrientation + * @see #setOrientation(int) + */ + public int getOrientation() { + return mOrientation; + } + + /** + * Sets the orientation of the layout. {@link android.support.v7.widget.LinearLayoutManager} + * will do its best to keep scroll position. + * + * @param orientation {@link #HORIZONTAL} or {@link #VERTICAL} + */ + public void setOrientation(int orientation) { + if (orientation != HORIZONTAL && orientation != VERTICAL) { + throw new IllegalArgumentException("invalid orientation:" + orientation); + } + assertNotInLayoutOrScroll(null); + if (orientation == mOrientation) { + return; + } + mOrientation = orientation; + mOrientationHelper = null; + requestLayout(); + } + + /** + * Calculates the view layout order. (e.g. from end to start or start to end) + * RTL layout support is applied automatically. So if layout is RTL and + * {@link #getReverseLayout()} is {@code true}, elements will be laid out starting from left. + */ + private void resolveShouldLayoutReverse() { + // A == B is the same result, but we rather keep it readable + if (mOrientation == VERTICAL || !isLayoutRTL()) { + mShouldReverseLayout = mReverseLayout; + } else { + mShouldReverseLayout = !mReverseLayout; + } + } + + /** + * Returns if views are laid out from the opposite direction of the layout. + * + * @return If layout is reversed or not. + * @see {@link #setReverseLayout(boolean)} + */ + public boolean getReverseLayout() { + return mReverseLayout; + } + + /** + * Used to reverse item traversal and layout order. + * This behaves similar to the layout change for RTL views. When set to true, first item is + * laid out at the end of the UI, second item is laid out before it etc. + * + * For horizontal layouts, it depends on the layout direction. + * When set to true, If {@link android.support.v7.widget.RecyclerView} is LTR, than it will + * layout from RTL, if {@link android.support.v7.widget.RecyclerView}} is RTL, it will layout + * from LTR. + * + * If you are looking for the exact same behavior of + * {@link android.widget.AbsListView#setStackFromBottom(boolean)}, use + * {@link #setStackFromEnd(boolean)} + */ + public void setReverseLayout(boolean reverseLayout) { + assertNotInLayoutOrScroll(null); + if (reverseLayout == mReverseLayout) { + return; + } + mReverseLayout = reverseLayout; + requestLayout(); + } + + /** + * {@inheritDoc} + */ + @Override + public View findViewByPosition(int position) { + final int childCount = getChildCount(); + if (childCount == 0) { + return null; + } + final int firstChild = getPosition(getChildAt(0)); + final int viewPosition = position - firstChild; + if (viewPosition >= 0 && viewPosition < childCount) { + return getChildAt(viewPosition); + } + return null; + } + + /** + *

Returns the amount of extra space that should be laid out by LayoutManager. + * By default, {@link android.support.v7.widget.LinearLayoutManager} lays out 1 extra page of + * items while smooth scrolling and 0 otherwise. You can override this method to implement your + * custom layout pre-cache logic.

+ *

Laying out invisible elements will eventually come with performance cost. On the other + * hand, in places like smooth scrolling to an unknown location, this extra content helps + * LayoutManager to calculate a much smoother scrolling; which improves user experience.

+ *

You can also use this if you are trying to pre-layout your upcoming views.

+ * + * @return The extra space that should be laid out (in pixels). + */ + protected int getExtraLayoutSpace(RecyclerView.State state) { + if (state.hasTargetScrollPosition()) { + return mOrientationHelper.getTotalSpace(); + } else { + return 0; + } + } + + @Override + public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, + int position) { + LinearSmoothScroller linearSmoothScroller = + new LinearSmoothScroller(recyclerView.getContext()) { + @Override + public PointF computeScrollVectorForPosition(int targetPosition) { + return LinearLayoutManager.this + .computeScrollVectorForPosition(targetPosition); + } + }; + linearSmoothScroller.setTargetPosition(position); + startSmoothScroll(linearSmoothScroller); + } + + public PointF computeScrollVectorForPosition(int targetPosition) { + if (getChildCount() == 0) { + return null; + } + final int firstChildPos = getPosition(getChildAt(0)); + final int direction = targetPosition < firstChildPos != mShouldReverseLayout ? -1 : 1; + if (mOrientation == HORIZONTAL) { + return new PointF(direction, 0); + } else { + return new PointF(0, direction); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { + // layout algorithm: + // 1) by checking children and other variables, find an anchor coordinate and an anchor + // item position. + // 2) fill towards start, stacking from bottom + // 3) fill towards end, stacking from top + // 4) scroll to fulfill requirements like stack from bottom. + // create layout state + if (DEBUG) { + Log.d(TAG, "is pre layout:" + state.isPreLayout()); + } + if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) { + mPendingScrollPosition = mPendingSavedState.mAnchorPosition; + } + + ensureLayoutState(); + mLayoutState.mRecycle = false; + // resolve layout direction + resolveShouldLayoutReverse(); + + mAnchorInfo.reset(); + mAnchorInfo.mLayoutFromEnd = mShouldReverseLayout ^ mStackFromEnd; + // calculate anchor position and coordinate + updateAnchorInfoForLayout(state, mAnchorInfo); + if (DEBUG) { + Log.d(TAG, "Anchor info:" + mAnchorInfo); + } + + // LLM may decide to layout items for "extra" pixels to account for scrolling target, + // caching or predictive animations. + int extraForStart; + int extraForEnd; + final int extra = getExtraLayoutSpace(state); + // If the previous scroll delta was less than zero, the extra space should be laid out + // at the start. Otherwise, it should be at the end. + if (mLayoutState.mLastScrollDelta >= 0) { + extraForEnd = extra; + extraForStart = 0; + } else { + extraForStart = extra; + extraForEnd = 0; + } + extraForStart += mOrientationHelper.getStartAfterPadding(); + extraForEnd += mOrientationHelper.getEndPadding(); + if (state.isPreLayout() && mPendingScrollPosition != NO_POSITION && + mPendingScrollPositionOffset != INVALID_OFFSET) { + // if the child is visible and we are going to move it around, we should layout + // extra items in the opposite direction to make sure new items animate nicely + // instead of just fading in + final View existing = findViewByPosition(mPendingScrollPosition); + if (existing != null) { + final int current; + final int upcomingOffset; + if (mShouldReverseLayout) { + current = mOrientationHelper.getEndAfterPadding() - + mOrientationHelper.getDecoratedEnd(existing); + upcomingOffset = current - mPendingScrollPositionOffset; + } else { + current = mOrientationHelper.getDecoratedStart(existing) + - mOrientationHelper.getStartAfterPadding(); + upcomingOffset = mPendingScrollPositionOffset - current; + } + if (upcomingOffset > 0) { + extraForStart += upcomingOffset; + } else { + extraForEnd -= upcomingOffset; + } + } + } + int startOffset; + int endOffset; + onAnchorReady(state, mAnchorInfo); + detachAndScrapAttachedViews(recycler); + mLayoutState.mIsPreLayout = state.isPreLayout(); + if (mAnchorInfo.mLayoutFromEnd) { + // fill towards start + updateLayoutStateToFillStart(mAnchorInfo); + mLayoutState.mExtra = extraForStart; + fill(recycler, mLayoutState, state, false); + startOffset = mLayoutState.mOffset; + final int firstElement = mLayoutState.mCurrentPosition; + if (mLayoutState.mAvailable > 0) { + extraForEnd += mLayoutState.mAvailable; + } + // fill towards end + updateLayoutStateToFillEnd(mAnchorInfo); + mLayoutState.mExtra = extraForEnd; + mLayoutState.mCurrentPosition += mLayoutState.mItemDirection; + fill(recycler, mLayoutState, state, false); + endOffset = mLayoutState.mOffset; + + if (mLayoutState.mAvailable > 0) { + // end could not consume all. add more items towards start + extraForStart = mLayoutState.mAvailable; + updateLayoutStateToFillStart(firstElement, startOffset); + mLayoutState.mExtra = extraForStart; + fill(recycler, mLayoutState, state, false); + startOffset = mLayoutState.mOffset; + } + } else { + // fill towards end + updateLayoutStateToFillEnd(mAnchorInfo); + mLayoutState.mExtra = extraForEnd; + fill(recycler, mLayoutState, state, false); + endOffset = mLayoutState.mOffset; + final int lastElement = mLayoutState.mCurrentPosition; + if (mLayoutState.mAvailable > 0) { + extraForStart += mLayoutState.mAvailable; + } + // fill towards start + updateLayoutStateToFillStart(mAnchorInfo); + mLayoutState.mExtra = extraForStart; + mLayoutState.mCurrentPosition += mLayoutState.mItemDirection; + fill(recycler, mLayoutState, state, false); + startOffset = mLayoutState.mOffset; + + if (mLayoutState.mAvailable > 0) { + extraForEnd = mLayoutState.mAvailable; + // start could not consume all it should. add more items towards end + updateLayoutStateToFillEnd(lastElement, endOffset); + mLayoutState.mExtra = extraForEnd; + fill(recycler, mLayoutState, state, false); + endOffset = mLayoutState.mOffset; + } + } + + // changes may cause gaps on the UI, try to fix them. + // TODO we can probably avoid this if neither stackFromEnd/reverseLayout/RTL values have + // changed + if (getChildCount() > 0) { + // because layout from end may be changed by scroll to position + // we re-calculate it. + // find which side we should check for gaps. + if (mShouldReverseLayout ^ mStackFromEnd) { + int fixOffset = fixLayoutEndGap(endOffset, recycler, state, true); + startOffset += fixOffset; + endOffset += fixOffset; + fixOffset = fixLayoutStartGap(startOffset, recycler, state, false); + startOffset += fixOffset; + endOffset += fixOffset; + } else { + int fixOffset = fixLayoutStartGap(startOffset, recycler, state, true); + startOffset += fixOffset; + endOffset += fixOffset; + fixOffset = fixLayoutEndGap(endOffset, recycler, state, false); + startOffset += fixOffset; + endOffset += fixOffset; + } + } + layoutForPredictiveAnimations(recycler, state, startOffset, endOffset); + if (!state.isPreLayout()) { + mPendingScrollPosition = NO_POSITION; + mPendingScrollPositionOffset = INVALID_OFFSET; + mOrientationHelper.onLayoutComplete(); + } + mLastStackFromEnd = mStackFromEnd; + mPendingSavedState = null; // we don't need this anymore + if (DEBUG) { + validateChildOrder(); + } + } + + /** + * Method called when Anchor position is decided. Extending class can setup accordingly or + * even update anchor info if necessary. + * + * @param state + * @param anchorInfo Simple data structure to keep anchor point information for the next layout + */ + void onAnchorReady(RecyclerView.State state, AnchorInfo anchorInfo) { + } + + /** + * If necessary, layouts new items for predictive animations + */ + private void layoutForPredictiveAnimations(RecyclerView.Recycler recycler, + RecyclerView.State state, int startOffset, int endOffset) { + // If there are scrap children that we did not layout, we need to find where they did go + // and layout them accordingly so that animations can work as expected. + // This case may happen if new views are added or an existing view expands and pushes + // another view out of bounds. + if (!state.willRunPredictiveAnimations() || getChildCount() == 0 || state.isPreLayout() + || !supportsPredictiveItemAnimations()) { + return; + } + // to make the logic simpler, we calculate the size of children and call fill. + int scrapExtraStart = 0, scrapExtraEnd = 0; + final List scrapList = recycler.getScrapList(); + final int scrapSize = scrapList.size(); + final int firstChildPos = getPosition(getChildAt(0)); + for (int i = 0; i < scrapSize; i++) { + RecyclerView.ViewHolder scrap = scrapList.get(i); + if (scrap.isRemoved()) { + continue; + } + final int position = scrap.getLayoutPosition(); + final int direction = position < firstChildPos != mShouldReverseLayout + ? LayoutState.LAYOUT_START : LayoutState.LAYOUT_END; + if (direction == LayoutState.LAYOUT_START) { + scrapExtraStart += mOrientationHelper.getDecoratedMeasurement(scrap.itemView); + } else { + scrapExtraEnd += mOrientationHelper.getDecoratedMeasurement(scrap.itemView); + } + } + + if (DEBUG) { + Log.d(TAG, "for unused scrap, decided to add " + scrapExtraStart + + " towards start and " + scrapExtraEnd + " towards end"); + } + mLayoutState.mScrapList = scrapList; + if (scrapExtraStart > 0) { + View anchor = getChildClosestToStart(); + updateLayoutStateToFillStart(getPosition(anchor), startOffset); + mLayoutState.mExtra = scrapExtraStart; + mLayoutState.mAvailable = 0; + mLayoutState.assignPositionFromScrapList(); + fill(recycler, mLayoutState, state, false); + } + + if (scrapExtraEnd > 0) { + View anchor = getChildClosestToEnd(); + updateLayoutStateToFillEnd(getPosition(anchor), endOffset); + mLayoutState.mExtra = scrapExtraEnd; + mLayoutState.mAvailable = 0; + mLayoutState.assignPositionFromScrapList(); + fill(recycler, mLayoutState, state, false); + } + mLayoutState.mScrapList = null; + } + + private void updateAnchorInfoForLayout(RecyclerView.State state, AnchorInfo anchorInfo) { + if (updateAnchorFromPendingData(state, anchorInfo)) { + if (DEBUG) { + Log.d(TAG, "updated anchor info from pending information"); + } + return; + } + + if (updateAnchorFromChildren(state, anchorInfo)) { + if (DEBUG) { + Log.d(TAG, "updated anchor info from existing children"); + } + return; + } + if (DEBUG) { + Log.d(TAG, "deciding anchor info for fresh state"); + } + anchorInfo.assignCoordinateFromPadding(); + anchorInfo.mPosition = mStackFromEnd ? state.getItemCount() - 1 : 0; + } + + /** + * Finds an anchor child from existing Views. Most of the time, this is the view closest to + * start or end that has a valid position (e.g. not removed). + *

+ * If a child has focus, it is given priority. + */ + private boolean updateAnchorFromChildren(RecyclerView.State state, AnchorInfo anchorInfo) { + if (getChildCount() == 0) { + return false; + } + final View focused = getFocusedChild(); + if (focused != null && anchorInfo.isViewValidAsAnchor(focused, state)) { + anchorInfo.assignFromViewAndKeepVisibleRect(focused); + return true; + } + if (mLastStackFromEnd != mStackFromEnd) { + return false; + } + View referenceChild = anchorInfo.mLayoutFromEnd ? findReferenceChildClosestToEnd(state) + : findReferenceChildClosestToStart(state); + if (referenceChild != null) { + anchorInfo.assignFromView(referenceChild); + // If all visible views are removed in 1 pass, reference child might be out of bounds. + // If that is the case, offset it back to 0 so that we use these pre-layout children. + if (!state.isPreLayout() && supportsPredictiveItemAnimations()) { + // validate this child is at least partially visible. if not, offset it to start + final boolean notVisible = + mOrientationHelper.getDecoratedStart(referenceChild) >= mOrientationHelper + .getEndAfterPadding() + || mOrientationHelper.getDecoratedEnd(referenceChild) + < mOrientationHelper.getStartAfterPadding(); + if (notVisible) { + anchorInfo.mCoordinate = anchorInfo.mLayoutFromEnd + ? mOrientationHelper.getEndAfterPadding() + : mOrientationHelper.getStartAfterPadding(); + } + } + return true; + } + return false; + } + + /** + * If there is a pending scroll position or saved states, updates the anchor info from that + * data and returns true + */ + private boolean updateAnchorFromPendingData(RecyclerView.State state, AnchorInfo anchorInfo) { + if (state.isPreLayout() || mPendingScrollPosition == NO_POSITION) { + return false; + } + // validate scroll position + if (mPendingScrollPosition < 0 || mPendingScrollPosition >= state.getItemCount()) { + mPendingScrollPosition = NO_POSITION; + mPendingScrollPositionOffset = INVALID_OFFSET; + if (DEBUG) { + Log.e(TAG, "ignoring invalid scroll position " + mPendingScrollPosition); + } + return false; + } + + // if child is visible, try to make it a reference child and ensure it is fully visible. + // if child is not visible, align it depending on its virtual position. + anchorInfo.mPosition = mPendingScrollPosition; + if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) { + // Anchor offset depends on how that child was laid out. Here, we update it + // according to our current view bounds + anchorInfo.mLayoutFromEnd = mPendingSavedState.mAnchorLayoutFromEnd; + if (anchorInfo.mLayoutFromEnd) { + anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding() - + mPendingSavedState.mAnchorOffset; + } else { + anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding() + + mPendingSavedState.mAnchorOffset; + } + return true; + } + + if (mPendingScrollPositionOffset == INVALID_OFFSET) { + View child = findViewByPosition(mPendingScrollPosition); + if (child != null) { + final int childSize = mOrientationHelper.getDecoratedMeasurement(child); + if (childSize > mOrientationHelper.getTotalSpace()) { + // item does not fit. fix depending on layout direction + anchorInfo.assignCoordinateFromPadding(); + return true; + } + final int startGap = mOrientationHelper.getDecoratedStart(child) + - mOrientationHelper.getStartAfterPadding(); + if (startGap < 0) { + anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding(); + anchorInfo.mLayoutFromEnd = false; + return true; + } + final int endGap = mOrientationHelper.getEndAfterPadding() - + mOrientationHelper.getDecoratedEnd(child); + if (endGap < 0) { + anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding(); + anchorInfo.mLayoutFromEnd = true; + return true; + } + anchorInfo.mCoordinate = anchorInfo.mLayoutFromEnd + ? (mOrientationHelper.getDecoratedEnd(child) + mOrientationHelper + .getTotalSpaceChange()) + : mOrientationHelper.getDecoratedStart(child); + } else { // item is not visible. + if (getChildCount() > 0) { + // get position of any child, does not matter + int pos = getPosition(getChildAt(0)); + anchorInfo.mLayoutFromEnd = mPendingScrollPosition < pos + == mShouldReverseLayout; + } + anchorInfo.assignCoordinateFromPadding(); + } + return true; + } + // override layout from end values for consistency + anchorInfo.mLayoutFromEnd = mShouldReverseLayout; + if (mShouldReverseLayout) { + anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding() - + mPendingScrollPositionOffset; + } else { + anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding() + + mPendingScrollPositionOffset; + } + return true; + } + + /** + * @return The final offset amount for children + */ + private int fixLayoutEndGap(int endOffset, RecyclerView.Recycler recycler, + RecyclerView.State state, boolean canOffsetChildren) { + int gap = mOrientationHelper.getEndAfterPadding() - endOffset; + int fixOffset = 0; + if (gap > 0) { + fixOffset = -scrollBy(-gap, recycler, state); + } else { + return 0; // nothing to fix + } + // move offset according to scroll amount + endOffset += fixOffset; + if (canOffsetChildren) { + // re-calculate gap, see if we could fix it + gap = mOrientationHelper.getEndAfterPadding() - endOffset; + if (gap > 0) { + mOrientationHelper.offsetChildren(gap); + return gap + fixOffset; + } + } + return fixOffset; + } + + /** + * @return The final offset amount for children + */ + private int fixLayoutStartGap(int startOffset, RecyclerView.Recycler recycler, + RecyclerView.State state, boolean canOffsetChildren) { + int gap = startOffset - mOrientationHelper.getStartAfterPadding(); + int fixOffset = 0; + if (gap > 0) { + // check if we should fix this gap. + fixOffset = -scrollBy(gap, recycler, state); + } else { + return 0; // nothing to fix + } + startOffset += fixOffset; + if (canOffsetChildren) { + // re-calculate gap, see if we could fix it + gap = startOffset - mOrientationHelper.getStartAfterPadding(); + if (gap > 0) { + mOrientationHelper.offsetChildren(-gap); + return fixOffset - gap; + } + } + return fixOffset; + } + + private void updateLayoutStateToFillEnd(AnchorInfo anchorInfo) { + updateLayoutStateToFillEnd(anchorInfo.mPosition, anchorInfo.mCoordinate); + } + + private void updateLayoutStateToFillEnd(int itemPosition, int offset) { + mLayoutState.mAvailable = mOrientationHelper.getEndAfterPadding() - offset; + mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD : + LayoutState.ITEM_DIRECTION_TAIL; + mLayoutState.mCurrentPosition = itemPosition; + mLayoutState.mLayoutDirection = LayoutState.LAYOUT_END; + mLayoutState.mOffset = offset; + mLayoutState.mScrollingOffset = LayoutState.SCOLLING_OFFSET_NaN; + } + + private void updateLayoutStateToFillStart(AnchorInfo anchorInfo) { + updateLayoutStateToFillStart(anchorInfo.mPosition, anchorInfo.mCoordinate); + } + + private void updateLayoutStateToFillStart(int itemPosition, int offset) { + mLayoutState.mAvailable = offset - mOrientationHelper.getStartAfterPadding(); + mLayoutState.mCurrentPosition = itemPosition; + mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL : + LayoutState.ITEM_DIRECTION_HEAD; + mLayoutState.mLayoutDirection = LayoutState.LAYOUT_START; + mLayoutState.mOffset = offset; + mLayoutState.mScrollingOffset = LayoutState.SCOLLING_OFFSET_NaN; + + } + + protected boolean isLayoutRTL() { + return getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL; + } + + void ensureLayoutState() { + if (mLayoutState == null) { + mLayoutState = createLayoutState(); + } + if (mOrientationHelper == null) { + mOrientationHelper = OrientationHelper.createOrientationHelper(this, mOrientation); + } + } + + /** + * Test overrides this to plug some tracking and verification. + * + * @return A new LayoutState + */ + LayoutState createLayoutState() { + return new LayoutState(); + } + + /** + *

Scroll the RecyclerView to make the position visible.

+ * + *

RecyclerView will scroll the minimum amount that is necessary to make the + * target position visible. If you are looking for a similar behavior to + * {@link android.widget.ListView#setSelection(int)} or + * {@link android.widget.ListView#setSelectionFromTop(int, int)}, use + * {@link #scrollToPositionWithOffset(int, int)}.

+ * + *

Note that scroll position change will not be reflected until the next layout call.

+ * + * @param position Scroll to this adapter position + * @see #scrollToPositionWithOffset(int, int) + */ + @Override + public void scrollToPosition(int position) { + mPendingScrollPosition = position; + mPendingScrollPositionOffset = INVALID_OFFSET; + if (mPendingSavedState != null) { + mPendingSavedState.invalidateAnchor(); + } + requestLayout(); + } + + /** + * Scroll to the specified adapter position with the given offset from resolved layout + * start. Resolved layout start depends on {@link #getReverseLayout()}, + * {@link ViewCompat#getLayoutDirection(android.view.View)} and {@link #getStackFromEnd()}. + *

+ * For example, if layout is {@link #VERTICAL} and {@link #getStackFromEnd()} is true, calling + * scrollToPositionWithOffset(10, 20) will layout such that + * item[10]'s bottom is 20 pixels above the RecyclerView's bottom. + *

+ * Note that scroll position change will not be reflected until the next layout call. + * + *

+ * If you are just trying to make a position visible, use {@link #scrollToPosition(int)}. + * + * @param position Index (starting at 0) of the reference item. + * @param offset The distance (in pixels) between the start edge of the item view and + * start edge of the RecyclerView. + * @see #setReverseLayout(boolean) + * @see #scrollToPosition(int) + */ + public void scrollToPositionWithOffset(int position, int offset) { + mPendingScrollPosition = position; + mPendingScrollPositionOffset = offset; + if (mPendingSavedState != null) { + mPendingSavedState.invalidateAnchor(); + } + requestLayout(); + } + + + /** + * {@inheritDoc} + */ + @Override + public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, + RecyclerView.State state) { + if (mOrientation == VERTICAL) { + return 0; + } + return scrollBy(dx, recycler, state); + } + + /** + * {@inheritDoc} + */ + @Override + public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, + RecyclerView.State state) { + if (mOrientation == HORIZONTAL) { + return 0; + } + return scrollBy(dy, recycler, state); + } + + @Override + public int computeHorizontalScrollOffset(RecyclerView.State state) { + return computeScrollOffset(state); + } + + @Override + public int computeVerticalScrollOffset(RecyclerView.State state) { + return computeScrollOffset(state); + } + + @Override + public int computeHorizontalScrollExtent(RecyclerView.State state) { + return computeScrollExtent(state); + } + + @Override + public int computeVerticalScrollExtent(RecyclerView.State state) { + return computeScrollExtent(state); + } + + @Override + public int computeHorizontalScrollRange(RecyclerView.State state) { + return computeScrollRange(state); + } + + @Override + public int computeVerticalScrollRange(RecyclerView.State state) { + return computeScrollRange(state); + } + + private int computeScrollOffset(RecyclerView.State state) { + if (getChildCount() == 0) { + return 0; + } + ensureLayoutState(); + return ScrollbarHelper.computeScrollOffset(state, mOrientationHelper, + findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true), + findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true), + this, mSmoothScrollbarEnabled, mShouldReverseLayout); + } + + private int computeScrollExtent(RecyclerView.State state) { + if (getChildCount() == 0) { + return 0; + } + ensureLayoutState(); + return ScrollbarHelper.computeScrollExtent(state, mOrientationHelper, + findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true), + findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true), + this, mSmoothScrollbarEnabled); + } + + private int computeScrollRange(RecyclerView.State state) { + if (getChildCount() == 0) { + return 0; + } + ensureLayoutState(); + return ScrollbarHelper.computeScrollRange(state, mOrientationHelper, + findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true), + findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true), + this, mSmoothScrollbarEnabled); + } + + /** + * When smooth scrollbar is enabled, the position and size of the scrollbar thumb is computed + * based on the number of visible pixels in the visible items. This however assumes that all + * list items have similar or equal widths or heights (depending on list orientation). + * If you use a list in which items have different dimensions, the scrollbar will change + * appearance as the user scrolls through the list. To avoid this issue, you need to disable + * this property. + * + * When smooth scrollbar is disabled, the position and size of the scrollbar thumb is based + * solely on the number of items in the adapter and the position of the visible items inside + * the adapter. This provides a stable scrollbar as the user navigates through a list of items + * with varying widths / heights. + * + * @param enabled Whether or not to enable smooth scrollbar. + * + * @see #setSmoothScrollbarEnabled(boolean) + */ + public void setSmoothScrollbarEnabled(boolean enabled) { + mSmoothScrollbarEnabled = enabled; + } + + /** + * Returns the current state of the smooth scrollbar feature. It is enabled by default. + * + * @return True if smooth scrollbar is enabled, false otherwise. + * + * @see #setSmoothScrollbarEnabled(boolean) + */ + public boolean isSmoothScrollbarEnabled() { + return mSmoothScrollbarEnabled; + } + + private void updateLayoutState(int layoutDirection, int requiredSpace, + boolean canUseExistingSpace, RecyclerView.State state) { + mLayoutState.mExtra = getExtraLayoutSpace(state); + mLayoutState.mLayoutDirection = layoutDirection; + int fastScrollSpace; + if (layoutDirection == LayoutState.LAYOUT_END) { + mLayoutState.mExtra += mOrientationHelper.getEndPadding(); + // get the first child in the direction we are going + final View child = getChildClosestToEnd(); + // the direction in which we are traversing children + mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD + : LayoutState.ITEM_DIRECTION_TAIL; + mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection; + mLayoutState.mOffset = mOrientationHelper.getDecoratedEnd(child); + // calculate how much we can scroll without adding new children (independent of layout) + fastScrollSpace = mOrientationHelper.getDecoratedEnd(child) + - mOrientationHelper.getEndAfterPadding(); + + } else { + final View child = getChildClosestToStart(); + mLayoutState.mExtra += mOrientationHelper.getStartAfterPadding(); + mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL + : LayoutState.ITEM_DIRECTION_HEAD; + mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection; + mLayoutState.mOffset = mOrientationHelper.getDecoratedStart(child); + fastScrollSpace = -mOrientationHelper.getDecoratedStart(child) + + mOrientationHelper.getStartAfterPadding(); + } + mLayoutState.mAvailable = requiredSpace; + if (canUseExistingSpace) { + mLayoutState.mAvailable -= fastScrollSpace; + } + mLayoutState.mScrollingOffset = fastScrollSpace; + } + + int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { + if (getChildCount() == 0 || dy == 0) { + return 0; + } + mLayoutState.mRecycle = true; + ensureLayoutState(); + final int layoutDirection = dy > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START; + final int absDy = Math.abs(dy); + updateLayoutState(layoutDirection, absDy, true, state); + final int freeScroll = mLayoutState.mScrollingOffset; + final int consumed = freeScroll + fill(recycler, mLayoutState, state, false); + if (consumed < 0) { + if (DEBUG) { + Log.d(TAG, "Don't have any more elements to scroll"); + } + return 0; + } + final int scrolled = absDy > consumed ? layoutDirection * consumed : dy; + mOrientationHelper.offsetChildren(-scrolled); + if (DEBUG) { + Log.d(TAG, "scroll req: " + dy + " scrolled: " + scrolled); + } + mLayoutState.mLastScrollDelta = scrolled; + return scrolled; + } + + @Override + public void assertNotInLayoutOrScroll(String message) { + if (mPendingSavedState == null) { + super.assertNotInLayoutOrScroll(message); + } + } + + /** + * Recycles children between given indices. + * + * @param startIndex inclusive + * @param endIndex exclusive + */ + private void recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex) { + if (startIndex == endIndex) { + return; + } + if (DEBUG) { + Log.d(TAG, "Recycling " + Math.abs(startIndex - endIndex) + " items"); + } + if (endIndex > startIndex) { + for (int i = endIndex - 1; i >= startIndex; i--) { + removeAndRecycleViewAt(i, recycler); + } + } else { + for (int i = startIndex; i > endIndex; i--) { + removeAndRecycleViewAt(i, recycler); + } + } + } + + /** + * Recycles views that went out of bounds after scrolling towards the end of the layout. + * + * @param recycler Recycler instance of {@link android.support.v7.widget.RecyclerView} + * @param dt This can be used to add additional padding to the visible area. This is used + * to + * detect children that will go out of bounds after scrolling, without actually + * moving them. + */ + private void recycleViewsFromStart(RecyclerView.Recycler recycler, int dt) { + if (dt < 0) { + if (DEBUG) { + Log.d(TAG, "Called recycle from start with a negative value. This might happen" + + " during layout changes but may be sign of a bug"); + } + return; + } + // ignore padding, ViewGroup may not clip children. + final int limit = dt; + final int childCount = getChildCount(); + if (mShouldReverseLayout) { + for (int i = childCount - 1; i >= 0; i--) { + View child = getChildAt(i); + if (mOrientationHelper.getDecoratedEnd(child) > limit) {// stop here + recycleChildren(recycler, childCount - 1, i); + return; + } + } + } else { + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + if (mOrientationHelper.getDecoratedEnd(child) > limit) {// stop here + recycleChildren(recycler, 0, i); + return; + } + } + } + } + + + /** + * Recycles views that went out of bounds after scrolling towards the start of the layout. + * + * @param recycler Recycler instance of {@link android.support.v7.widget.RecyclerView} + * @param dt This can be used to add additional padding to the visible area. This is used + * to detect children that will go out of bounds after scrolling, without + * actually moving them. + */ + private void recycleViewsFromEnd(RecyclerView.Recycler recycler, int dt) { + final int childCount = getChildCount(); + if (dt < 0) { + if (DEBUG) { + Log.d(TAG, "Called recycle from end with a negative value. This might happen" + + " during layout changes but may be sign of a bug"); + } + return; + } + final int limit = mOrientationHelper.getEnd() - dt; + if (mShouldReverseLayout) { + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + if (mOrientationHelper.getDecoratedStart(child) < limit) {// stop here + recycleChildren(recycler, 0, i); + return; + } + } + } else { + for (int i = childCount - 1; i >= 0; i--) { + View child = getChildAt(i); + if (mOrientationHelper.getDecoratedStart(child) < limit) {// stop here + recycleChildren(recycler, childCount - 1, i); + return; + } + } + } + + } + + /** + * Helper method to call appropriate recycle method depending on current layout direction + * + * @param recycler Current recycler that is attached to RecyclerView + * @param layoutState Current layout state. Right now, this object does not change but + * we may consider moving it out of this view so passing around as a + * parameter for now, rather than accessing {@link #mLayoutState} + * @see #recycleViewsFromStart(android.support.v7.widget.RecyclerView.Recycler, int) + * @see #recycleViewsFromEnd(android.support.v7.widget.RecyclerView.Recycler, int) + * @see android.support.v7.widget.LinearLayoutManager.LayoutState#mLayoutDirection + */ + private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) { + if (!layoutState.mRecycle) { + return; + } + if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { + recycleViewsFromEnd(recycler, layoutState.mScrollingOffset); + } else { + recycleViewsFromStart(recycler, layoutState.mScrollingOffset); + } + } + + /** + * The magic functions :). Fills the given layout, defined by the layoutState. This is fairly + * independent from the rest of the {@link android.support.v7.widget.LinearLayoutManager} + * and with little change, can be made publicly available as a helper class. + * + * @param recycler Current recycler that is attached to RecyclerView + * @param layoutState Configuration on how we should fill out the available space. + * @param state Context passed by the RecyclerView to control scroll steps. + * @param stopOnFocusable If true, filling stops in the first focusable new child + * @return Number of pixels that it added. Useful for scoll functions. + */ + int fill(RecyclerView.Recycler recycler, LayoutState layoutState, + RecyclerView.State state, boolean stopOnFocusable) { + // max offset we should set is mFastScroll + available + final int start = layoutState.mAvailable; + if (layoutState.mScrollingOffset != LayoutState.SCOLLING_OFFSET_NaN) { + // TODO ugly bug fix. should not happen + if (layoutState.mAvailable < 0) { + layoutState.mScrollingOffset += layoutState.mAvailable; + } + recycleByLayoutState(recycler, layoutState); + } + int remainingSpace = layoutState.mAvailable + layoutState.mExtra; + LayoutChunkResult layoutChunkResult = new LayoutChunkResult(); + while (remainingSpace > 0 && layoutState.hasMore(state)) { + layoutChunkResult.resetInternal(); + layoutChunk(recycler, state, layoutState, layoutChunkResult); + if (layoutChunkResult.mFinished) { + break; + } + layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection; + /** + * Consume the available space if: + * * layoutChunk did not request to be ignored + * * OR we are laying out scrap children + * * OR we are not doing pre-layout + */ + if (!layoutChunkResult.mIgnoreConsumed || mLayoutState.mScrapList != null + || !state.isPreLayout()) { + layoutState.mAvailable -= layoutChunkResult.mConsumed; + // we keep a separate remaining space because mAvailable is important for recycling + remainingSpace -= layoutChunkResult.mConsumed; + } + + if (layoutState.mScrollingOffset != LayoutState.SCOLLING_OFFSET_NaN) { + layoutState.mScrollingOffset += layoutChunkResult.mConsumed; + if (layoutState.mAvailable < 0) { + layoutState.mScrollingOffset += layoutState.mAvailable; + } + recycleByLayoutState(recycler, layoutState); + } + if (stopOnFocusable && layoutChunkResult.mFocusable) { + break; + } + } + if (DEBUG) { + validateChildOrder(); + } + return start - layoutState.mAvailable; + } + + void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, + LayoutState layoutState, LayoutChunkResult result) { + View view = layoutState.next(recycler); + if (view == null) { + if (DEBUG && layoutState.mScrapList == null) { + throw new RuntimeException("received null view when unexpected"); + } + // if we are laying out views in scrap, this may return null which means there is + // no more items to layout. + result.mFinished = true; + return; + } + RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams(); + if (layoutState.mScrapList == null) { + if (mShouldReverseLayout == (layoutState.mLayoutDirection + == LayoutState.LAYOUT_START)) { + addView(view); + } else { + addView(view, 0); + } + } else { + if (mShouldReverseLayout == (layoutState.mLayoutDirection + == LayoutState.LAYOUT_START)) { + addDisappearingView(view); + } else { + addDisappearingView(view, 0); + } + } + measureChildWithMargins(view, 0, 0); + result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view); + int left, top, right, bottom; + if (mOrientation == VERTICAL) { + if (isLayoutRTL()) { + right = getWidth() - getPaddingRight(); + left = right - mOrientationHelper.getDecoratedMeasurementInOther(view); + } else { + left = getPaddingLeft(); + right = left + mOrientationHelper.getDecoratedMeasurementInOther(view); + } + if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { + bottom = layoutState.mOffset; + top = layoutState.mOffset - result.mConsumed; + } else { + top = layoutState.mOffset; + bottom = layoutState.mOffset + result.mConsumed; + } + } else { + top = getPaddingTop(); + bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view); + + if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { + right = layoutState.mOffset; + left = layoutState.mOffset - result.mConsumed; + } else { + left = layoutState.mOffset; + right = layoutState.mOffset + result.mConsumed; + } + } + // We calculate everything with View's bounding box (which includes decor and margins) + // To calculate correct layout position, we subtract margins. + layoutDecorated(view, left + params.leftMargin, top + params.topMargin, + right - params.rightMargin, bottom - params.bottomMargin); + if (DEBUG) { + Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:" + + (left + params.leftMargin) + ", t:" + (top + params.topMargin) + ", r:" + + (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin)); + } + // Consume the available space if the view is not removed OR changed + if (params.isItemRemoved() || params.isItemChanged()) { + result.mIgnoreConsumed = true; + } + result.mFocusable = view.isFocusable(); + } + + /** + * Converts a focusDirection to orientation. + * + * @param focusDirection One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN}, + * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, + * {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD} + * or 0 for not applicable + * @return {@link LayoutState#LAYOUT_START} or {@link LayoutState#LAYOUT_END} if focus direction + * is applicable to current state, {@link LayoutState#INVALID_LAYOUT} otherwise. + */ + private int convertFocusDirectionToLayoutDirection(int focusDirection) { + switch (focusDirection) { + case View.FOCUS_BACKWARD: + return LayoutState.LAYOUT_START; + case View.FOCUS_FORWARD: + return LayoutState.LAYOUT_END; + case View.FOCUS_UP: + return mOrientation == VERTICAL ? LayoutState.LAYOUT_START + : LayoutState.INVALID_LAYOUT; + case View.FOCUS_DOWN: + return mOrientation == VERTICAL ? LayoutState.LAYOUT_END + : LayoutState.INVALID_LAYOUT; + case View.FOCUS_LEFT: + return mOrientation == HORIZONTAL ? LayoutState.LAYOUT_START + : LayoutState.INVALID_LAYOUT; + case View.FOCUS_RIGHT: + return mOrientation == HORIZONTAL ? LayoutState.LAYOUT_END + : LayoutState.INVALID_LAYOUT; + default: + if (DEBUG) { + Log.d(TAG, "Unknown focus request:" + focusDirection); + } + return LayoutState.INVALID_LAYOUT; + } + + } + + /** + * Convenience method to find the child closes to start. Caller should check it has enough + * children. + * + * @return The child closes to start of the layout from user's perspective. + */ + private View getChildClosestToStart() { + return getChildAt(mShouldReverseLayout ? getChildCount() - 1 : 0); + } + + /** + * Convenience method to find the child closes to end. Caller should check it has enough + * children. + * + * @return The child closes to end of the layout from user's perspective. + */ + private View getChildClosestToEnd() { + return getChildAt(mShouldReverseLayout ? 0 : getChildCount() - 1); + } + + /** + * Convenience method to find the visible child closes to start. Caller should check if it has + * enough children. + * + * @param completelyVisible Whether child should be completely visible or not + * @return The first visible child closest to start of the layout from user's perspective. + */ + private View findFirstVisibleChildClosestToStart(boolean completelyVisible, + boolean acceptPartiallyVisible) { + if (mShouldReverseLayout) { + return findOneVisibleChild(getChildCount() - 1, -1, completelyVisible, + acceptPartiallyVisible); + } else { + return findOneVisibleChild(0, getChildCount(), completelyVisible, + acceptPartiallyVisible); + } + } + + /** + * Convenience method to find the visible child closes to end. Caller should check if it has + * enough children. + * + * @param completelyVisible Whether child should be completely visible or not + * @return The first visible child closest to end of the layout from user's perspective. + */ + private View findFirstVisibleChildClosestToEnd(boolean completelyVisible, + boolean acceptPartiallyVisible) { + if (mShouldReverseLayout) { + return findOneVisibleChild(0, getChildCount(), completelyVisible, + acceptPartiallyVisible); + } else { + return findOneVisibleChild(getChildCount() - 1, -1, completelyVisible, + acceptPartiallyVisible); + } + } + + + /** + * Among the children that are suitable to be considered as an anchor child, returns the one + * closest to the end of the layout. + *

+ * Due to ambiguous adapter updates or children being removed, some children's positions may be + * invalid. This method is a best effort to find a position within adapter bounds if possible. + *

+ * It also prioritizes children that are within the visible bounds. + * @return A View that can be used an an anchor View. + */ + private View findReferenceChildClosestToEnd(RecyclerView.State state) { + return mShouldReverseLayout ? findFirstReferenceChild(state.getItemCount()) : + findLastReferenceChild(state.getItemCount()); + } + + /** + * Among the children that are suitable to be considered as an anchor child, returns the one + * closest to the start of the layout. + *

+ * Due to ambiguous adapter updates or children being removed, some children's positions may be + * invalid. This method is a best effort to find a position within adapter bounds if possible. + *

+ * It also prioritizes children that are within the visible bounds. + * + * @return A View that can be used an an anchor View. + */ + private View findReferenceChildClosestToStart(RecyclerView.State state) { + return mShouldReverseLayout ? findLastReferenceChild(state.getItemCount()) : + findFirstReferenceChild(state.getItemCount()); + } + + private View findFirstReferenceChild(int itemCount) { + return findReferenceChild(0, getChildCount(), itemCount); + } + + private View findLastReferenceChild(int itemCount) { + return findReferenceChild(getChildCount() - 1, -1, itemCount); + } + + // overridden by GridLayoutManager + View findReferenceChild(int start, int end, int itemCount) { + ensureLayoutState(); + View invalidMatch = null; + View outOfBoundsMatch = null; + final int boundsStart = mOrientationHelper.getStartAfterPadding(); + final int boundsEnd = mOrientationHelper.getEndAfterPadding(); + final int diff = end > start ? 1 : -1; + for (int i = start; i != end; i += diff) { + final View view = getChildAt(i); + final int position = getPosition(view); + if (position >= 0 && position < itemCount) { + if (((RecyclerView.LayoutParams) view.getLayoutParams()).isItemRemoved()) { + if (invalidMatch == null) { + invalidMatch = view; // removed item, least preferred + } + } else if (mOrientationHelper.getDecoratedStart(view) >= boundsEnd || + mOrientationHelper.getDecoratedEnd(view) < boundsStart) { + if (outOfBoundsMatch == null) { + outOfBoundsMatch = view; // item is not visible, less preferred + } + } else { + return view; + } + } + } + return outOfBoundsMatch != null ? outOfBoundsMatch : invalidMatch; + } + + /** + * Returns the adapter position of the first visible view. This position does not include + * adapter changes that were dispatched after the last layout pass. + *

+ * Note that, this value is not affected by layout orientation or item order traversal. + * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter, + * not in the layout. + *

+ * If RecyclerView has item decorators, they will be considered in calculations as well. + *

+ * LayoutManager may pre-cache some views that are not necessarily visible. Those views + * are ignored in this method. + * + * @return The adapter position of the first visible item or {@link RecyclerView#NO_POSITION} if + * there aren't any visible items. + * @see #findFirstCompletelyVisibleItemPosition() + * @see #findLastVisibleItemPosition() + */ + public int findFirstVisibleItemPosition() { + final View child = findOneVisibleChild(0, getChildCount(), false, true); + return child == null ? NO_POSITION : getPosition(child); + } + + /** + * Returns the adapter position of the first fully visible view. This position does not include + * adapter changes that were dispatched after the last layout pass. + *

+ * Note that bounds check is only performed in the current orientation. That means, if + * LayoutManager is horizontal, it will only check the view's left and right edges. + * + * @return The adapter position of the first fully visible item or + * {@link RecyclerView#NO_POSITION} if there aren't any visible items. + * @see #findFirstVisibleItemPosition() + * @see #findLastCompletelyVisibleItemPosition() + */ + public int findFirstCompletelyVisibleItemPosition() { + final View child = findOneVisibleChild(0, getChildCount(), true, false); + return child == null ? NO_POSITION : getPosition(child); + } + + /** + * Returns the adapter position of the last visible view. This position does not include + * adapter changes that were dispatched after the last layout pass. + *

+ * Note that, this value is not affected by layout orientation or item order traversal. + * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter, + * not in the layout. + *

+ * If RecyclerView has item decorators, they will be considered in calculations as well. + *

+ * LayoutManager may pre-cache some views that are not necessarily visible. Those views + * are ignored in this method. + * + * @return The adapter position of the last visible view or {@link RecyclerView#NO_POSITION} if + * there aren't any visible items. + * @see #findLastCompletelyVisibleItemPosition() + * @see #findFirstVisibleItemPosition() + */ + public int findLastVisibleItemPosition() { + final View child = findOneVisibleChild(getChildCount() - 1, -1, false, true); + return child == null ? NO_POSITION : getPosition(child); + } + + /** + * Returns the adapter position of the last fully visible view. This position does not include + * adapter changes that were dispatched after the last layout pass. + *

+ * Note that bounds check is only performed in the current orientation. That means, if + * LayoutManager is horizontal, it will only check the view's left and right edges. + * + * @return The adapter position of the last fully visible view or + * {@link RecyclerView#NO_POSITION} if there aren't any visible items. + * @see #findLastVisibleItemPosition() + * @see #findFirstCompletelyVisibleItemPosition() + */ + public int findLastCompletelyVisibleItemPosition() { + final View child = findOneVisibleChild(getChildCount() - 1, -1, true, false); + return child == null ? NO_POSITION : getPosition(child); + } + + View findOneVisibleChild(int fromIndex, int toIndex, boolean completelyVisible, + boolean acceptPartiallyVisible) { + ensureLayoutState(); + final int start = mOrientationHelper.getStartAfterPadding(); + final int end = mOrientationHelper.getEndAfterPadding(); + final int next = toIndex > fromIndex ? 1 : -1; + View partiallyVisible = null; + for (int i = fromIndex; i != toIndex; i+=next) { + final View child = getChildAt(i); + final int childStart = mOrientationHelper.getDecoratedStart(child); + final int childEnd = mOrientationHelper.getDecoratedEnd(child); + if (childStart < end && childEnd > start) { + if (completelyVisible) { + if (childStart >= start && childEnd <= end) { + return child; + } else if (acceptPartiallyVisible && partiallyVisible == null) { + partiallyVisible = child; + } + } else { + return child; + } + } + } + return partiallyVisible; + } + + @Override + public View onFocusSearchFailed(View focused, int focusDirection, + RecyclerView.Recycler recycler, RecyclerView.State state) { + resolveShouldLayoutReverse(); + if (getChildCount() == 0) { + return null; + } + + final int layoutDir = convertFocusDirectionToLayoutDirection(focusDirection); + if (layoutDir == LayoutState.INVALID_LAYOUT) { + return null; + } + ensureLayoutState(); + final View referenceChild; + if (layoutDir == LayoutState.LAYOUT_START) { + referenceChild = findReferenceChildClosestToStart(state); + } else { + referenceChild = findReferenceChildClosestToEnd(state); + } + if (referenceChild == null) { + if (DEBUG) { + Log.d(TAG, + "Cannot find a child with a valid position to be used for focus search."); + } + return null; + } + ensureLayoutState(); + final int maxScroll = (int) (MAX_SCROLL_FACTOR * mOrientationHelper.getTotalSpace()); + updateLayoutState(layoutDir, maxScroll, false, state); + mLayoutState.mScrollingOffset = LayoutState.SCOLLING_OFFSET_NaN; + mLayoutState.mRecycle = false; + fill(recycler, mLayoutState, state, true); + final View nextFocus; + if (layoutDir == LayoutState.LAYOUT_START) { + nextFocus = getChildClosestToStart(); + } else { + nextFocus = getChildClosestToEnd(); + } + if (nextFocus == referenceChild || !nextFocus.isFocusable()) { + return null; + } + return nextFocus; + } + + /** + * Used for debugging. + * Logs the internal representation of children to default logger. + */ + private void logChildren() { + Log.d(TAG, "internal representation of views on the screen"); + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + Log.d(TAG, "item " + getPosition(child) + ", coord:" + + mOrientationHelper.getDecoratedStart(child)); + } + Log.d(TAG, "=============="); + } + + /** + * Used for debugging. + * Validates that child views are laid out in correct order. This is important because rest of + * the algorithm relies on this constraint. + * + * In default layout, child 0 should be closest to screen position 0 and last child should be + * closest to position WIDTH or HEIGHT. + * In reverse layout, last child should be closes to screen position 0 and first child should + * be closest to position WIDTH or HEIGHT + */ + void validateChildOrder() { + Log.d(TAG, "validating child count " + getChildCount()); + if (getChildCount() < 1) { + return; + } + int lastPos = getPosition(getChildAt(0)); + int lastScreenLoc = mOrientationHelper.getDecoratedStart(getChildAt(0)); + if (mShouldReverseLayout) { + for (int i = 1; i < getChildCount(); i++) { + View child = getChildAt(i); + int pos = getPosition(child); + int screenLoc = mOrientationHelper.getDecoratedStart(child); + if (pos < lastPos) { + logChildren(); + throw new RuntimeException("detected invalid position. loc invalid? " + + (screenLoc < lastScreenLoc)); + } + if (screenLoc > lastScreenLoc) { + logChildren(); + throw new RuntimeException("detected invalid location"); + } + } + } else { + for (int i = 1; i < getChildCount(); i++) { + View child = getChildAt(i); + int pos = getPosition(child); + int screenLoc = mOrientationHelper.getDecoratedStart(child); + if (pos < lastPos) { + logChildren(); + throw new RuntimeException("detected invalid position. loc invalid? " + + (screenLoc < lastScreenLoc)); + } + if (screenLoc < lastScreenLoc) { + logChildren(); + throw new RuntimeException("detected invalid location"); + } + } + } + } + + @Override + public boolean supportsPredictiveItemAnimations() { + return mPendingSavedState == null && mLastStackFromEnd == mStackFromEnd; + } + + /** + * Helper class that keeps temporary state while {LayoutManager} is filling out the empty + * space. + */ + static class LayoutState { + + final static String TAG = "LinearLayoutManager#LayoutState"; + + final static int LAYOUT_START = -1; + + final static int LAYOUT_END = 1; + + final static int INVALID_LAYOUT = Integer.MIN_VALUE; + + final static int ITEM_DIRECTION_HEAD = -1; + + final static int ITEM_DIRECTION_TAIL = 1; + + final static int SCOLLING_OFFSET_NaN = Integer.MIN_VALUE; + + /** + * We may not want to recycle children in some cases (e.g. layout) + */ + boolean mRecycle = true; + + /** + * Pixel offset where layout should start + */ + int mOffset; + + /** + * Number of pixels that we should fill, in the layout direction. + */ + int mAvailable; + + /** + * Current position on the adapter to get the next item. + */ + int mCurrentPosition; + + /** + * Defines the direction in which the data adapter is traversed. + * Should be {@link #ITEM_DIRECTION_HEAD} or {@link #ITEM_DIRECTION_TAIL} + */ + int mItemDirection; + + /** + * Defines the direction in which the layout is filled. + * Should be {@link #LAYOUT_START} or {@link #LAYOUT_END} + */ + int mLayoutDirection; + + /** + * Used when LayoutState is constructed in a scrolling state. + * It should be set the amount of scrolling we can make without creating a new view. + * Settings this is required for efficient view recycling. + */ + int mScrollingOffset; + + /** + * Used if you want to pre-layout items that are not yet visible. + * The difference with {@link #mAvailable} is that, when recycling, distance laid out for + * {@link #mExtra} is not considered to avoid recycling visible children. + */ + int mExtra = 0; + + /** + * Equal to {@link RecyclerView.State#isPreLayout()}. When consuming scrap, if this value + * is set to true, we skip removed views since they should not be laid out in post layout + * step. + */ + boolean mIsPreLayout = false; + + /** + * The most recent {@link #scrollBy(int, RecyclerView.Recycler, RecyclerView.State)} amount. + */ + int mLastScrollDelta; + + /** + * When LLM needs to layout particular views, it sets this list in which case, LayoutState + * will only return views from this list and return null if it cannot find an item. + */ + List mScrapList = null; + + /** + * @return true if there are more items in the data adapter + */ + boolean hasMore(RecyclerView.State state) { + return mCurrentPosition >= 0 && mCurrentPosition < state.getItemCount(); + } + + /** + * Gets the view for the next element that we should layout. + * Also updates current item index to the next item, based on {@link #mItemDirection} + * + * @return The next element that we should layout. + */ + View next(RecyclerView.Recycler recycler) { + if (mScrapList != null) { + return nextViewFromScrapList(); + } + final View view = recycler.getViewForPosition(mCurrentPosition); + mCurrentPosition += mItemDirection; + return view; + } + + /** + * Returns the next item from the scrap list. + *

+ * Upon finding a valid VH, sets current item position to VH.itemPosition + mItemDirection + * + * @return View if an item in the current position or direction exists if not null. + */ + private View nextViewFromScrapList() { + final int size = mScrapList.size(); + for (int i = 0; i < size; i++) { + final RecyclerView.ViewHolder viewHolder = mScrapList.get(i); + if (viewHolder.isRemoved()) { + continue; + } + if (mCurrentPosition == viewHolder.getLayoutPosition()) { + assignPositionFromScrapList(viewHolder); + return viewHolder.itemView; + } + } + return null; + } + + public void assignPositionFromScrapList() { + assignPositionFromScrapList(null); + } + + public void assignPositionFromScrapList(RecyclerView.ViewHolder ignore) { + RecyclerView.ViewHolder closest = nextViewHolderInLimitedList(ignore); + mCurrentPosition = closest == null ? RecyclerView.NO_POSITION : + closest.getLayoutPosition(); + } + + public RecyclerView.ViewHolder nextViewHolderInLimitedList(RecyclerView.ViewHolder ignore) { + int size = mScrapList.size(); + RecyclerView.ViewHolder closest = null; + int closestDistance = Integer.MAX_VALUE; + if (DEBUG && mIsPreLayout) { + throw new IllegalStateException("Scrap list cannot be used in pre layout"); + } + for (int i = 0; i < size; i++) { + RecyclerView.ViewHolder viewHolder = mScrapList.get(i); + if (viewHolder == ignore || viewHolder.isRemoved()) { + continue; + } + final int distance = (viewHolder.getLayoutPosition() - mCurrentPosition) * + mItemDirection; + if (distance < 0) { + continue; // item is not in current direction + } + if (distance < closestDistance) { + closest = viewHolder; + closestDistance = distance; + if (distance == 0) { + break; + } + } + } + return closest; + } + + void log() { + Log.d(TAG, "avail:" + mAvailable + ", ind:" + mCurrentPosition + ", dir:" + + mItemDirection + ", offset:" + mOffset + ", layoutDir:" + mLayoutDirection); + } + } + + static class SavedState implements Parcelable { + + int mAnchorPosition; + + int mAnchorOffset; + + boolean mAnchorLayoutFromEnd; + + public SavedState() { + + } + + SavedState(Parcel in) { + mAnchorPosition = in.readInt(); + mAnchorOffset = in.readInt(); + mAnchorLayoutFromEnd = in.readInt() == 1; + } + + public SavedState(SavedState other) { + mAnchorPosition = other.mAnchorPosition; + mAnchorOffset = other.mAnchorOffset; + mAnchorLayoutFromEnd = other.mAnchorLayoutFromEnd; + } + + boolean hasValidAnchor() { + return mAnchorPosition >= 0; + } + + void invalidateAnchor() { + mAnchorPosition = NO_POSITION; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mAnchorPosition); + dest.writeInt(mAnchorOffset); + dest.writeInt(mAnchorLayoutFromEnd ? 1 : 0); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + @Override + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } + + /** + * Simple data class to keep Anchor information + */ + class AnchorInfo { + int mPosition; + int mCoordinate; + boolean mLayoutFromEnd; + void reset() { + mPosition = NO_POSITION; + mCoordinate = INVALID_OFFSET; + mLayoutFromEnd = false; + } + + /** + * assigns anchor coordinate from the RecyclerView's padding depending on current + * layoutFromEnd value + */ + void assignCoordinateFromPadding() { + mCoordinate = mLayoutFromEnd + ? mOrientationHelper.getEndAfterPadding() + : mOrientationHelper.getStartAfterPadding(); + } + + @Override + public String toString() { + return "AnchorInfo{" + + "mPosition=" + mPosition + + ", mCoordinate=" + mCoordinate + + ", mLayoutFromEnd=" + mLayoutFromEnd + + '}'; + } + + private boolean isViewValidAsAnchor(View child, RecyclerView.State state) { + RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams(); + return !lp.isItemRemoved() && lp.getViewLayoutPosition() >= 0 + && lp.getViewLayoutPosition() < state.getItemCount(); + } + + public void assignFromViewAndKeepVisibleRect(View child) { + final int spaceChange = mOrientationHelper.getTotalSpaceChange(); + if (spaceChange >= 0) { + assignFromView(child); + return; + } + mPosition = getPosition(child); + if (mLayoutFromEnd) { + final int prevLayoutEnd = mOrientationHelper.getEndAfterPadding() - spaceChange; + final int childEnd = mOrientationHelper.getDecoratedEnd(child); + final int previousEndMargin = prevLayoutEnd - childEnd; + mCoordinate = mOrientationHelper.getEndAfterPadding() - previousEndMargin; + // ensure we did not push child's top out of bounds because of this + if (previousEndMargin > 0) {// we have room to shift bottom if necessary + final int childSize = mOrientationHelper.getDecoratedMeasurement(child); + final int estimatedChildStart = mCoordinate - childSize; + final int layoutStart = mOrientationHelper.getStartAfterPadding(); + final int previousStartMargin = mOrientationHelper.getDecoratedStart(child) - + layoutStart; + final int startReference = layoutStart + Math.min(previousStartMargin, 0); + final int startMargin = estimatedChildStart - startReference; + if (startMargin < 0) { + // offset to make top visible but not too much + mCoordinate += Math.min(previousEndMargin, -startMargin); + } + } + } else { + final int childStart = mOrientationHelper.getDecoratedStart(child); + final int startMargin = childStart - mOrientationHelper.getStartAfterPadding(); + mCoordinate = childStart; + if (startMargin > 0) { // we have room to fix end as well + final int estimatedEnd = childStart + + mOrientationHelper.getDecoratedMeasurement(child); + final int previousLayoutEnd = mOrientationHelper.getEndAfterPadding() - + spaceChange; + final int previousEndMargin = previousLayoutEnd - + mOrientationHelper.getDecoratedEnd(child); + final int endReference = mOrientationHelper.getEndAfterPadding() - + Math.min(0, previousEndMargin); + final int endMargin = endReference - estimatedEnd; + if (endMargin < 0) { + mCoordinate -= Math.min(startMargin, -endMargin); + } + } + } + } + + public void assignFromView(View child) { + if (mLayoutFromEnd) { + mCoordinate = mOrientationHelper.getDecoratedEnd(child) + + mOrientationHelper.getTotalSpaceChange(); + } else { + mCoordinate = mOrientationHelper.getDecoratedStart(child); + } + + mPosition = getPosition(child); + } + } + + protected static class LayoutChunkResult { + public int mConsumed; + public boolean mFinished; + public boolean mIgnoreConsumed; + public boolean mFocusable; + + void resetInternal() { + mConsumed = 0; + mFinished = false; + mIgnoreConsumed = false; + mFocusable = false; + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/LinearSmoothScroller.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/LinearSmoothScroller.java new file mode 100644 index 000000000..419d9447d --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/LinearSmoothScroller.java @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.telegram.android.support.widget; + +import android.content.Context; +import android.graphics.PointF; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.View; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.LinearInterpolator; + +/** + * {@link RecyclerView.SmoothScroller} implementation which uses + * {@link android.view.animation.LinearInterpolator} until the target position becames a child of + * the RecyclerView and then uses + * {@link android.view.animation.DecelerateInterpolator} to slowly approach to target position. + */ +abstract public class LinearSmoothScroller extends RecyclerView.SmoothScroller { + + private static final String TAG = "LinearSmoothScroller"; + + private static final boolean DEBUG = false; + + private static final float MILLISECONDS_PER_INCH = 25f; + + private static final int TARGET_SEEK_SCROLL_DISTANCE_PX = 10000; + + /** + * Align child view's left or top with parent view's left or top + * + * @see #calculateDtToFit(int, int, int, int, int) + * @see #calculateDxToMakeVisible(android.view.View, int) + * @see #calculateDyToMakeVisible(android.view.View, int) + */ + public static final int SNAP_TO_START = -1; + + /** + * Align child view's right or bottom with parent view's right or bottom + * + * @see #calculateDtToFit(int, int, int, int, int) + * @see #calculateDxToMakeVisible(android.view.View, int) + * @see #calculateDyToMakeVisible(android.view.View, int) + */ + public static final int SNAP_TO_END = 1; + + /** + *

Decides if the child should be snapped from start or end, depending on where it + * currently is in relation to its parent.

+ *

For instance, if the view is virtually on the left of RecyclerView, using + * {@code SNAP_TO_ANY} is the same as using {@code SNAP_TO_START}

+ * + * @see #calculateDtToFit(int, int, int, int, int) + * @see #calculateDxToMakeVisible(android.view.View, int) + * @see #calculateDyToMakeVisible(android.view.View, int) + */ + public static final int SNAP_TO_ANY = 0; + + // Trigger a scroll to a further distance than TARGET_SEEK_SCROLL_DISTANCE_PX so that if target + // view is not laid out until interim target position is reached, we can detect the case before + // scrolling slows down and reschedule another interim target scroll + private static final float TARGET_SEEK_EXTRA_SCROLL_RATIO = 1.2f; + + protected final LinearInterpolator mLinearInterpolator = new LinearInterpolator(); + + protected final DecelerateInterpolator mDecelerateInterpolator = new DecelerateInterpolator(); + + protected PointF mTargetVector; + + private final float MILLISECONDS_PER_PX; + + // Temporary variables to keep track of the interim scroll target. These values do not + // point to a real item position, rather point to an estimated location pixels. + protected int mInterimTargetDx = 0, mInterimTargetDy = 0; + + public LinearSmoothScroller(Context context) { + MILLISECONDS_PER_PX = calculateSpeedPerPixel(context.getResources().getDisplayMetrics()); + } + + /** + * {@inheritDoc} + */ + @Override + protected void onStart() { + + } + + /** + * {@inheritDoc} + */ + @Override + protected void onTargetFound(View targetView, RecyclerView.State state, Action action) { + final int dx = calculateDxToMakeVisible(targetView, getHorizontalSnapPreference()); + final int dy = calculateDyToMakeVisible(targetView, getVerticalSnapPreference()); + final int distance = (int) Math.sqrt(dx * dx + dy * dy); + final int time = calculateTimeForDeceleration(distance); + if (time > 0) { + action.update(-dx, -dy, time, mDecelerateInterpolator); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void onSeekTargetStep(int dx, int dy, RecyclerView.State state, Action action) { + if (getChildCount() == 0) { + stop(); + return; + } + if (DEBUG && mTargetVector != null + && ((mTargetVector.x * dx < 0 || mTargetVector.y * dy < 0))) { + throw new IllegalStateException("Scroll happened in the opposite direction" + + " of the target. Some calculations are wrong"); + } + mInterimTargetDx = clampApplyScroll(mInterimTargetDx, dx); + mInterimTargetDy = clampApplyScroll(mInterimTargetDy, dy); + + if (mInterimTargetDx == 0 && mInterimTargetDy == 0) { + updateActionForInterimTarget(action); + } // everything is valid, keep going + + } + + /** + * {@inheritDoc} + */ + @Override + protected void onStop() { + mInterimTargetDx = mInterimTargetDy = 0; + mTargetVector = null; + } + + /** + * Calculates the scroll speed. + * + * @param displayMetrics DisplayMetrics to be used for real dimension calculations + * @return The time (in ms) it should take for each pixel. For instance, if returned value is + * 2 ms, it means scrolling 1000 pixels with LinearInterpolation should take 2 seconds. + */ + protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) { + return MILLISECONDS_PER_INCH / displayMetrics.densityDpi; + } + + /** + *

Calculates the time for deceleration so that transition from LinearInterpolator to + * DecelerateInterpolator looks smooth.

+ * + * @param dx Distance to scroll + * @return Time for DecelerateInterpolator to smoothly traverse the distance when transitioning + * from LinearInterpolation + */ + protected int calculateTimeForDeceleration(int dx) { + // we want to cover same area with the linear interpolator for the first 10% of the + // interpolation. After that, deceleration will take control. + // area under curve (1-(1-x)^2) can be calculated as (1 - x/3) * x * x + // which gives 0.100028 when x = .3356 + // this is why we divide linear scrolling time with .3356 + return (int) Math.ceil(calculateTimeForScrolling(dx) / .3356); + } + + /** + * Calculates the time it should take to scroll the given distance (in pixels) + * + * @param dx Distance in pixels that we want to scroll + * @return Time in milliseconds + * @see #calculateSpeedPerPixel(android.util.DisplayMetrics) + */ + protected int calculateTimeForScrolling(int dx) { + // In a case where dx is very small, rounding may return 0 although dx > 0. + // To avoid that issue, ceil the result so that if dx > 0, we'll always return positive + // time. + return (int) Math.ceil(Math.abs(dx) * MILLISECONDS_PER_PX); + } + + /** + * When scrolling towards a child view, this method defines whether we should align the left + * or the right edge of the child with the parent RecyclerView. + * + * @return SNAP_TO_START, SNAP_TO_END or SNAP_TO_ANY; depending on the current target vector + * @see #SNAP_TO_START + * @see #SNAP_TO_END + * @see #SNAP_TO_ANY + */ + protected int getHorizontalSnapPreference() { + return mTargetVector == null || mTargetVector.x == 0 ? SNAP_TO_ANY : + mTargetVector.x > 0 ? SNAP_TO_END : SNAP_TO_START; + } + + /** + * When scrolling towards a child view, this method defines whether we should align the top + * or the bottom edge of the child with the parent RecyclerView. + * + * @return SNAP_TO_START, SNAP_TO_END or SNAP_TO_ANY; depending on the current target vector + * @see #SNAP_TO_START + * @see #SNAP_TO_END + * @see #SNAP_TO_ANY + */ + protected int getVerticalSnapPreference() { + return mTargetVector == null || mTargetVector.y == 0 ? SNAP_TO_ANY : + mTargetVector.y > 0 ? SNAP_TO_END : SNAP_TO_START; + } + + /** + * When the target scroll position is not a child of the RecyclerView, this method calculates + * a direction vector towards that child and triggers a smooth scroll. + * + * @see #computeScrollVectorForPosition(int) + */ + protected void updateActionForInterimTarget(Action action) { + // find an interim target position + PointF scrollVector = computeScrollVectorForPosition(getTargetPosition()); + if (scrollVector == null || (scrollVector.x == 0 && scrollVector.y == 0)) { + Log.e(TAG, "To support smooth scrolling, you should override \n" + + "LayoutManager#computeScrollVectorForPosition.\n" + + "Falling back to instant scroll"); + final int target = getTargetPosition(); + stop(); + instantScrollToPosition(target); + return; + } + normalize(scrollVector); + mTargetVector = scrollVector; + + mInterimTargetDx = (int) (TARGET_SEEK_SCROLL_DISTANCE_PX * scrollVector.x); + mInterimTargetDy = (int) (TARGET_SEEK_SCROLL_DISTANCE_PX * scrollVector.y); + final int time = calculateTimeForScrolling(TARGET_SEEK_SCROLL_DISTANCE_PX); + // To avoid UI hiccups, trigger a smooth scroll to a distance little further than the + // interim target. Since we track the distance travelled in onSeekTargetStep callback, it + // won't actually scroll more than what we need. + action.update((int) (mInterimTargetDx * TARGET_SEEK_EXTRA_SCROLL_RATIO) + , (int) (mInterimTargetDy * TARGET_SEEK_EXTRA_SCROLL_RATIO) + , (int) (time * TARGET_SEEK_EXTRA_SCROLL_RATIO), mLinearInterpolator); + } + + private int clampApplyScroll(int tmpDt, int dt) { + final int before = tmpDt; + tmpDt -= dt; + if (before * tmpDt <= 0) { // changed sign, reached 0 or was 0, reset + return 0; + } + return tmpDt; + } + + /** + * Helper method for {@link #calculateDxToMakeVisible(android.view.View, int)} and + * {@link #calculateDyToMakeVisible(android.view.View, int)} + */ + public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int + snapPreference) { + switch (snapPreference) { + case SNAP_TO_START: + return boxStart - viewStart; + case SNAP_TO_END: + return boxEnd - viewEnd; + case SNAP_TO_ANY: + final int dtStart = boxStart - viewStart; + if (dtStart > 0) { + return dtStart; + } + final int dtEnd = boxEnd - viewEnd; + if (dtEnd < 0) { + return dtEnd; + } + break; + default: + throw new IllegalArgumentException("snap preference should be one of the" + + " constants defined in SmoothScroller, starting with SNAP_"); + } + return 0; + } + + /** + * Calculates the vertical scroll amount necessary to make the given view fully visible + * inside the RecyclerView. + * + * @param view The view which we want to make fully visible + * @param snapPreference The edge which the view should snap to when entering the visible + * area. One of {@link #SNAP_TO_START}, {@link #SNAP_TO_END} or + * {@link #SNAP_TO_END}. + * @return The vertical scroll amount necessary to make the view visible with the given + * snap preference. + */ + public int calculateDyToMakeVisible(View view, int snapPreference) { + final RecyclerView.LayoutManager layoutManager = getLayoutManager(); + if (!layoutManager.canScrollVertically()) { + return 0; + } + final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) + view.getLayoutParams(); + final int top = layoutManager.getDecoratedTop(view) - params.topMargin; + final int bottom = layoutManager.getDecoratedBottom(view) + params.bottomMargin; + final int start = layoutManager.getPaddingTop(); + final int end = layoutManager.getHeight() - layoutManager.getPaddingBottom(); + return calculateDtToFit(top, bottom, start, end, snapPreference); + } + + /** + * Calculates the horizontal scroll amount necessary to make the given view fully visible + * inside the RecyclerView. + * + * @param view The view which we want to make fully visible + * @param snapPreference The edge which the view should snap to when entering the visible + * area. One of {@link #SNAP_TO_START}, {@link #SNAP_TO_END} or + * {@link #SNAP_TO_END} + * @return The vertical scroll amount necessary to make the view visible with the given + * snap preference. + */ + public int calculateDxToMakeVisible(View view, int snapPreference) { + final RecyclerView.LayoutManager layoutManager = getLayoutManager(); + if (!layoutManager.canScrollHorizontally()) { + return 0; + } + final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) + view.getLayoutParams(); + final int left = layoutManager.getDecoratedLeft(view) - params.leftMargin; + final int right = layoutManager.getDecoratedRight(view) + params.rightMargin; + final int start = layoutManager.getPaddingLeft(); + final int end = layoutManager.getWidth() - layoutManager.getPaddingRight(); + return calculateDtToFit(left, right, start, end, snapPreference); + } + + abstract public PointF computeScrollVectorForPosition(int targetPosition); +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/OpReorderer.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/OpReorderer.java new file mode 100644 index 000000000..3d56822f7 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/OpReorderer.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.telegram.android.support.widget; + +import java.util.List; + +import org.telegram.android.support.widget.AdapterHelper.UpdateOp; +import static org.telegram.android.support.widget.AdapterHelper.UpdateOp.ADD; +import static org.telegram.android.support.widget.AdapterHelper.UpdateOp.MOVE; +import static org.telegram.android.support.widget.AdapterHelper.UpdateOp.REMOVE; +import static org.telegram.android.support.widget.AdapterHelper.UpdateOp.UPDATE; + +class OpReorderer { + + final Callback mCallback; + + public OpReorderer(Callback callback) { + mCallback = callback; + } + + void reorderOps(List ops) { + // since move operations breaks continuity, their effects on ADD/RM are hard to handle. + // we push them to the end of the list so that they can be handled easily. + int badMove; + while ((badMove = getLastMoveOutOfOrder(ops)) != -1) { + swapMoveOp(ops, badMove, badMove + 1); + } + } + + private void swapMoveOp(List list, int badMove, int next) { + final UpdateOp moveOp = list.get(badMove); + final UpdateOp nextOp = list.get(next); + switch (nextOp.cmd) { + case REMOVE: + swapMoveRemove(list, badMove, moveOp, next, nextOp); + break; + case ADD: + swapMoveAdd(list, badMove, moveOp, next, nextOp); + break; + case UPDATE: + swapMoveUpdate(list, badMove, moveOp, next, nextOp); + break; + } + } + + void swapMoveRemove(List list, int movePos, UpdateOp moveOp, + int removePos, UpdateOp removeOp) { + UpdateOp extraRm = null; + // check if move is nulled out by remove + boolean revertedMove = false; + final boolean moveIsBackwards; + + if (moveOp.positionStart < moveOp.itemCount) { + moveIsBackwards = false; + if (removeOp.positionStart == moveOp.positionStart + && removeOp.itemCount == moveOp.itemCount - moveOp.positionStart) { + revertedMove = true; + } + } else { + moveIsBackwards = true; + if (removeOp.positionStart == moveOp.itemCount + 1 && + removeOp.itemCount == moveOp.positionStart - moveOp.itemCount) { + revertedMove = true; + } + } + + // going in reverse, first revert the effect of add + if (moveOp.itemCount < removeOp.positionStart) { + removeOp.positionStart--; + } else if (moveOp.itemCount < removeOp.positionStart + removeOp.itemCount) { + // move is removed. + removeOp.itemCount --; + moveOp.cmd = REMOVE; + moveOp.itemCount = 1; + if (removeOp.itemCount == 0) { + list.remove(removePos); + mCallback.recycleUpdateOp(removeOp); + } + // no need to swap, it is already a remove + return; + } + + // now affect of add is consumed. now apply effect of first remove + if (moveOp.positionStart <= removeOp.positionStart) { + removeOp.positionStart++; + } else if (moveOp.positionStart < removeOp.positionStart + removeOp.itemCount) { + final int remaining = removeOp.positionStart + removeOp.itemCount + - moveOp.positionStart; + extraRm = mCallback.obtainUpdateOp(REMOVE, moveOp.positionStart + 1, remaining); + removeOp.itemCount = moveOp.positionStart - removeOp.positionStart; + } + + // if effects of move is reverted by remove, we are done. + if (revertedMove) { + list.set(movePos, removeOp); + list.remove(removePos); + mCallback.recycleUpdateOp(moveOp); + return; + } + + // now find out the new locations for move actions + if (moveIsBackwards) { + if (extraRm != null) { + if (moveOp.positionStart > extraRm.positionStart) { + moveOp.positionStart -= extraRm.itemCount; + } + if (moveOp.itemCount > extraRm.positionStart) { + moveOp.itemCount -= extraRm.itemCount; + } + } + if (moveOp.positionStart > removeOp.positionStart) { + moveOp.positionStart -= removeOp.itemCount; + } + if (moveOp.itemCount > removeOp.positionStart) { + moveOp.itemCount -= removeOp.itemCount; + } + } else { + if (extraRm != null) { + if (moveOp.positionStart >= extraRm.positionStart) { + moveOp.positionStart -= extraRm.itemCount; + } + if (moveOp.itemCount >= extraRm.positionStart) { + moveOp.itemCount -= extraRm.itemCount; + } + } + if (moveOp.positionStart >= removeOp.positionStart) { + moveOp.positionStart -= removeOp.itemCount; + } + if (moveOp.itemCount >= removeOp.positionStart) { + moveOp.itemCount -= removeOp.itemCount; + } + } + + list.set(movePos, removeOp); + if (moveOp.positionStart != moveOp.itemCount) { + list.set(removePos, moveOp); + } else { + list.remove(removePos); + } + if (extraRm != null) { + list.add(movePos, extraRm); + } + } + + private void swapMoveAdd(List list, int move, UpdateOp moveOp, int add, + UpdateOp addOp) { + int offset = 0; + // going in reverse, first revert the effect of add + if (moveOp.itemCount < addOp.positionStart) { + offset--; + } + if (moveOp.positionStart < addOp.positionStart) { + offset++; + } + if (addOp.positionStart <= moveOp.positionStart) { + moveOp.positionStart += addOp.itemCount; + } + if (addOp.positionStart <= moveOp.itemCount) { + moveOp.itemCount += addOp.itemCount; + } + addOp.positionStart += offset; + list.set(move, addOp); + list.set(add, moveOp); + } + + void swapMoveUpdate(List list, int move, UpdateOp moveOp, int update, + UpdateOp updateOp) { + UpdateOp extraUp1 = null; + UpdateOp extraUp2 = null; + // going in reverse, first revert the effect of add + if (moveOp.itemCount < updateOp.positionStart) { + updateOp.positionStart--; + } else if (moveOp.itemCount < updateOp.positionStart + updateOp.itemCount) { + // moved item is updated. add an update for it + updateOp.itemCount--; + extraUp1 = mCallback.obtainUpdateOp(UPDATE, moveOp.positionStart, 1); + } + // now affect of add is consumed. now apply effect of first remove + if (moveOp.positionStart <= updateOp.positionStart) { + updateOp.positionStart++; + } else if (moveOp.positionStart < updateOp.positionStart + updateOp.itemCount) { + final int remaining = updateOp.positionStart + updateOp.itemCount + - moveOp.positionStart; + extraUp2 = mCallback.obtainUpdateOp(UPDATE, moveOp.positionStart + 1, remaining); + updateOp.itemCount -= remaining; + } + list.set(update, moveOp); + if (updateOp.itemCount > 0) { + list.set(move, updateOp); + } else { + list.remove(move); + mCallback.recycleUpdateOp(updateOp); + } + if (extraUp1 != null) { + list.add(move, extraUp1); + } + if (extraUp2 != null) { + list.add(move, extraUp2); + } + } + + private int getLastMoveOutOfOrder(List list) { + boolean foundNonMove = false; + for (int i = list.size() - 1; i >= 0; i--) { + final UpdateOp op1 = list.get(i); + if (op1.cmd == MOVE) { + if (foundNonMove) { + return i; + } + } else { + foundNonMove = true; + } + } + return -1; + } + + static interface Callback { + + UpdateOp obtainUpdateOp(int cmd, int startPosition, int itemCount); + + void recycleUpdateOp(UpdateOp op); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/OrientationHelper.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/OrientationHelper.java new file mode 100644 index 000000000..bfe380636 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/OrientationHelper.java @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.telegram.android.support.widget; + +import android.view.View; +import android.widget.LinearLayout; + +/** + * Helper class for LayoutManagers to abstract measurements depending on the View's orientation. + *

+ * It is developed to easily support vertical and horizontal orientations in a LayoutManager but + * can also be used to abstract calls around view bounds and child measurements with margins and + * decorations. + * + * @see #createHorizontalHelper(RecyclerView.LayoutManager) + * @see #createVerticalHelper(RecyclerView.LayoutManager) + */ +public abstract class OrientationHelper { + + private static final int INVALID_SIZE = Integer.MIN_VALUE; + + protected final RecyclerView.LayoutManager mLayoutManager; + + public static final int HORIZONTAL = LinearLayout.HORIZONTAL; + + public static final int VERTICAL = LinearLayout.VERTICAL; + + private int mLastTotalSpace = INVALID_SIZE; + + private OrientationHelper(RecyclerView.LayoutManager layoutManager) { + mLayoutManager = layoutManager; + } + + /** + * Call this method after onLayout method is complete if state is NOT pre-layout. + * This method records information like layout bounds that might be useful in the next layout + * calculations. + */ + public void onLayoutComplete() { + mLastTotalSpace = getTotalSpace(); + } + + /** + * Returns the layout space change between the previous layout pass and current layout pass. + *

+ * Make sure you call {@link #onLayoutComplete()} at the end of your LayoutManager's + * {@link RecyclerView.LayoutManager#onLayoutChildren(RecyclerView.Recycler, + * RecyclerView.State)} method. + * + * @return The difference between the current total space and previous layout's total space. + * @see #onLayoutComplete() + */ + public int getTotalSpaceChange() { + return INVALID_SIZE == mLastTotalSpace ? 0 : getTotalSpace() - mLastTotalSpace; + } + + /** + * Returns the start of the view including its decoration and margin. + *

+ * For example, for the horizontal helper, if a View's left is at pixel 20, has 2px left + * decoration and 3px left margin, returned value will be 15px. + * + * @param view The view element to check + * @return The first pixel of the element + * @see #getDecoratedEnd(android.view.View) + */ + public abstract int getDecoratedStart(View view); + + /** + * Returns the end of the view including its decoration and margin. + *

+ * For example, for the horizontal helper, if a View's right is at pixel 200, has 2px right + * decoration and 3px right margin, returned value will be 205. + * + * @param view The view element to check + * @return The last pixel of the element + * @see #getDecoratedStart(android.view.View) + */ + public abstract int getDecoratedEnd(View view); + + /** + * Returns the space occupied by this View in the current orientation including decorations and + * margins. + * + * @param view The view element to check + * @return Total space occupied by this view + * @see #getDecoratedMeasurementInOther(View) + */ + public abstract int getDecoratedMeasurement(View view); + + /** + * Returns the space occupied by this View in the perpendicular orientation including + * decorations and margins. + * + * @param view The view element to check + * @return Total space occupied by this view in the perpendicular orientation to current one + * @see #getDecoratedMeasurement(View) + */ + public abstract int getDecoratedMeasurementInOther(View view); + + /** + * Returns the start position of the layout after the start padding is added. + * + * @return The very first pixel we can draw. + */ + public abstract int getStartAfterPadding(); + + /** + * Returns the end position of the layout after the end padding is removed. + * + * @return The end boundary for this layout. + */ + public abstract int getEndAfterPadding(); + + /** + * Returns the end position of the layout without taking padding into account. + * + * @return The end boundary for this layout without considering padding. + */ + public abstract int getEnd(); + + /** + * Offsets all children's positions by the given amount. + * + * @param amount Value to add to each child's layout parameters + */ + public abstract void offsetChildren(int amount); + + /** + * Returns the total space to layout. This number is the difference between + * {@link #getEndAfterPadding()} and {@link #getStartAfterPadding()}. + * + * @return Total space to layout children + */ + public abstract int getTotalSpace(); + + /** + * Offsets the child in this orientation. + * + * @param view View to offset + * @param offset offset amount + */ + public abstract void offsetChild(View view, int offset); + + /** + * Returns the padding at the end of the layout. For horizontal helper, this is the right + * padding and for vertical helper, this is the bottom padding. This method does not check + * whether the layout is RTL or not. + * + * @return The padding at the end of the layout. + */ + public abstract int getEndPadding(); + + /** + * Creates an OrientationHelper for the given LayoutManager and orientation. + * + * @param layoutManager LayoutManager to attach to + * @param orientation Desired orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL} + * @return A new OrientationHelper + */ + public static OrientationHelper createOrientationHelper( + RecyclerView.LayoutManager layoutManager, int orientation) { + switch (orientation) { + case HORIZONTAL: + return createHorizontalHelper(layoutManager); + case VERTICAL: + return createVerticalHelper(layoutManager); + } + throw new IllegalArgumentException("invalid orientation"); + } + + /** + * Creates a horizontal OrientationHelper for the given LayoutManager. + * + * @param layoutManager The LayoutManager to attach to. + * @return A new OrientationHelper + */ + public static OrientationHelper createHorizontalHelper( + RecyclerView.LayoutManager layoutManager) { + return new OrientationHelper(layoutManager) { + @Override + public int getEndAfterPadding() { + return mLayoutManager.getWidth() - mLayoutManager.getPaddingRight(); + } + + @Override + public int getEnd() { + return mLayoutManager.getWidth(); + } + + @Override + public void offsetChildren(int amount) { + mLayoutManager.offsetChildrenHorizontal(amount); + } + + @Override + public int getStartAfterPadding() { + return mLayoutManager.getPaddingLeft(); + } + + @Override + public int getDecoratedMeasurement(View view) { + final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) + view.getLayoutParams(); + return mLayoutManager.getDecoratedMeasuredWidth(view) + params.leftMargin + + params.rightMargin; + } + + @Override + public int getDecoratedMeasurementInOther(View view) { + final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) + view.getLayoutParams(); + return mLayoutManager.getDecoratedMeasuredHeight(view) + params.topMargin + + params.bottomMargin; + } + + @Override + public int getDecoratedEnd(View view) { + final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) + view.getLayoutParams(); + return mLayoutManager.getDecoratedRight(view) + params.rightMargin; + } + + @Override + public int getDecoratedStart(View view) { + final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) + view.getLayoutParams(); + return mLayoutManager.getDecoratedLeft(view) - params.leftMargin; + } + + @Override + public int getTotalSpace() { + return mLayoutManager.getWidth() - mLayoutManager.getPaddingLeft() + - mLayoutManager.getPaddingRight(); + } + + @Override + public void offsetChild(View view, int offset) { + view.offsetLeftAndRight(offset); + } + + @Override + public int getEndPadding() { + return mLayoutManager.getPaddingRight(); + } + }; + } + + /** + * Creates a vertical OrientationHelper for the given LayoutManager. + * + * @param layoutManager The LayoutManager to attach to. + * @return A new OrientationHelper + */ + public static OrientationHelper createVerticalHelper(RecyclerView.LayoutManager layoutManager) { + return new OrientationHelper(layoutManager) { + @Override + public int getEndAfterPadding() { + return mLayoutManager.getHeight() - mLayoutManager.getPaddingBottom(); + } + + @Override + public int getEnd() { + return mLayoutManager.getHeight(); + } + + @Override + public void offsetChildren(int amount) { + mLayoutManager.offsetChildrenVertical(amount); + } + + @Override + public int getStartAfterPadding() { + return mLayoutManager.getPaddingTop(); + } + + @Override + public int getDecoratedMeasurement(View view) { + final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) + view.getLayoutParams(); + return mLayoutManager.getDecoratedMeasuredHeight(view) + params.topMargin + + params.bottomMargin; + } + + @Override + public int getDecoratedMeasurementInOther(View view) { + final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) + view.getLayoutParams(); + return mLayoutManager.getDecoratedMeasuredWidth(view) + params.leftMargin + + params.rightMargin; + } + + @Override + public int getDecoratedEnd(View view) { + final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) + view.getLayoutParams(); + return mLayoutManager.getDecoratedBottom(view) + params.bottomMargin; + } + + @Override + public int getDecoratedStart(View view) { + final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) + view.getLayoutParams(); + return mLayoutManager.getDecoratedTop(view) - params.topMargin; + } + + @Override + public int getTotalSpace() { + return mLayoutManager.getHeight() - mLayoutManager.getPaddingTop() + - mLayoutManager.getPaddingBottom(); + } + + @Override + public void offsetChild(View view, int offset) { + view.offsetTopAndBottom(offset); + } + + @Override + public int getEndPadding() { + return mLayoutManager.getPaddingBottom(); + } + }; + } +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/PositionMap.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/PositionMap.java new file mode 100644 index 000000000..b97bd5a65 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/PositionMap.java @@ -0,0 +1,460 @@ +/* + * Copyright (C) 2013 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.android.support.widget; + +import java.util.ArrayList; + +/** + * Like a SparseArray, but with the ability to offset key ranges for bulk insertions/deletions. + */ +class PositionMap implements Cloneable { + private static final Object DELETED = new Object(); + private boolean mGarbage = false; + + private int[] mKeys; + private Object[] mValues; + private int mSize; + + /** + * Creates a new SparseArray containing no mappings. + */ + public PositionMap() { + this(10); + } + + /** + * Creates a new PositionMap containing no mappings that will not + * require any additional memory allocation to store the specified + * number of mappings. If you supply an initial capacity of 0, the + * sparse array will be initialized with a light-weight representation + * not requiring any additional array allocations. + */ + public PositionMap(int initialCapacity) { + if (initialCapacity == 0) { + mKeys = ContainerHelpers.EMPTY_INTS; + mValues = ContainerHelpers.EMPTY_OBJECTS; + } else { + initialCapacity = idealIntArraySize(initialCapacity); + mKeys = new int[initialCapacity]; + mValues = new Object[initialCapacity]; + } + mSize = 0; + } + + @Override + @SuppressWarnings("unchecked") + public PositionMap clone() { + PositionMap clone = null; + try { + clone = (PositionMap) super.clone(); + clone.mKeys = mKeys.clone(); + clone.mValues = mValues.clone(); + } catch (CloneNotSupportedException cnse) { + /* ignore */ + } + return clone; + } + + /** + * Gets the Object mapped from the specified key, or null + * if no such mapping has been made. + */ + public E get(int key) { + return get(key, null); + } + + /** + * Gets the Object mapped from the specified key, or the specified Object + * if no such mapping has been made. + */ + @SuppressWarnings("unchecked") + public E get(int key, E valueIfKeyNotFound) { + int i = ContainerHelpers.binarySearch(mKeys, mSize, key); + + if (i < 0 || mValues[i] == DELETED) { + return valueIfKeyNotFound; + } else { + return (E) mValues[i]; + } + } + + /** + * Removes the mapping from the specified key, if there was any. + */ + public void delete(int key) { + int i = ContainerHelpers.binarySearch(mKeys, mSize, key); + + if (i >= 0) { + if (mValues[i] != DELETED) { + mValues[i] = DELETED; + mGarbage = true; + } + } + } + + /** + * Alias for {@link #delete(int)}. + */ + public void remove(int key) { + delete(key); + } + + /** + * Removes the mapping at the specified index. + */ + public void removeAt(int index) { + if (mValues[index] != DELETED) { + mValues[index] = DELETED; + mGarbage = true; + } + } + + /** + * Remove a range of mappings as a batch. + * + * @param index Index to begin at + * @param size Number of mappings to remove + */ + public void removeAtRange(int index, int size) { + final int end = Math.min(mSize, index + size); + for (int i = index; i < end; i++) { + removeAt(i); + } + } + + public void insertKeyRange(int keyStart, int count) { + + } + + public void removeKeyRange(ArrayList removedItems, int keyStart, int count) { + + } + + private void gc() { + // Log.e("SparseArray", "gc start with " + mSize); + + int n = mSize; + int o = 0; + int[] keys = mKeys; + Object[] values = mValues; + + for (int i = 0; i < n; i++) { + Object val = values[i]; + + if (val != DELETED) { + if (i != o) { + keys[o] = keys[i]; + values[o] = val; + values[i] = null; + } + + o++; + } + } + + mGarbage = false; + mSize = o; + + // Log.e("SparseArray", "gc end with " + mSize); + } + + /** + * Adds a mapping from the specified key to the specified value, + * replacing the previous mapping from the specified key if there + * was one. + */ + public void put(int key, E value) { + int i = ContainerHelpers.binarySearch(mKeys, mSize, key); + + if (i >= 0) { + mValues[i] = value; + } else { + i = ~i; + + if (i < mSize && mValues[i] == DELETED) { + mKeys[i] = key; + mValues[i] = value; + return; + } + + if (mGarbage && mSize >= mKeys.length) { + gc(); + + // Search again because indices may have changed. + i = ~ContainerHelpers.binarySearch(mKeys, mSize, key); + } + + if (mSize >= mKeys.length) { + int n = idealIntArraySize(mSize + 1); + + int[] nkeys = new int[n]; + Object[] nvalues = new Object[n]; + + // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } + + if (mSize - i != 0) { + // Log.e("SparseArray", "move " + (mSize - i)); + System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i); + System.arraycopy(mValues, i, mValues, i + 1, mSize - i); + } + + mKeys[i] = key; + mValues[i] = value; + mSize++; + } + } + + /** + * Returns the number of key-value mappings that this SparseArray + * currently stores. + */ + public int size() { + if (mGarbage) { + gc(); + } + + return mSize; + } + + /** + * Given an index in the range 0...size()-1, returns + * the key from the indexth key-value mapping that this + * SparseArray stores. + */ + public int keyAt(int index) { + if (mGarbage) { + gc(); + } + + return mKeys[index]; + } + + /** + * Given an index in the range 0...size()-1, returns + * the value from the indexth key-value mapping that this + * SparseArray stores. + */ + @SuppressWarnings("unchecked") + public E valueAt(int index) { + if (mGarbage) { + gc(); + } + + return (E) mValues[index]; + } + + /** + * Given an index in the range 0...size()-1, sets a new + * value for the indexth key-value mapping that this + * SparseArray stores. + */ + public void setValueAt(int index, E value) { + if (mGarbage) { + gc(); + } + + mValues[index] = value; + } + + /** + * Returns the index for which {@link #keyAt} would return the + * specified key, or a negative number if the specified + * key is not mapped. + */ + public int indexOfKey(int key) { + if (mGarbage) { + gc(); + } + + return ContainerHelpers.binarySearch(mKeys, mSize, key); + } + + /** + * Returns an index for which {@link #valueAt} would return the + * specified key, or a negative number if no keys map to the + * specified value. + *

Beware that this is a linear search, unlike lookups by key, + * and that multiple keys can map to the same value and this will + * find only one of them. + *

Note also that unlike most collections' {@code indexOf} methods, + * this method compares values using {@code ==} rather than {@code equals}. + */ + public int indexOfValue(E value) { + if (mGarbage) { + gc(); + } + + for (int i = 0; i < mSize; i++) + if (mValues[i] == value) + return i; + + return -1; + } + + /** + * Removes all key-value mappings from this SparseArray. + */ + public void clear() { + int n = mSize; + Object[] values = mValues; + + for (int i = 0; i < n; i++) { + values[i] = null; + } + + mSize = 0; + mGarbage = false; + } + + /** + * Puts a key/value pair into the array, optimizing for the case where + * the key is greater than all existing keys in the array. + */ + public void append(int key, E value) { + if (mSize != 0 && key <= mKeys[mSize - 1]) { + put(key, value); + return; + } + + if (mGarbage && mSize >= mKeys.length) { + gc(); + } + + int pos = mSize; + if (pos >= mKeys.length) { + int n = idealIntArraySize(pos + 1); + + int[] nkeys = new int[n]; + Object[] nvalues = new Object[n]; + + // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } + + mKeys[pos] = key; + mValues[pos] = value; + mSize = pos + 1; + } + + /** + * {@inheritDoc} + * + *

This implementation composes a string by iterating over its mappings. If + * this map contains itself as a value, the string "(this Map)" + * will appear in its place. + */ + @Override + public String toString() { + if (size() <= 0) { + return "{}"; + } + + StringBuilder buffer = new StringBuilder(mSize * 28); + buffer.append('{'); + for (int i=0; i 0) { + buffer.append(", "); + } + int key = keyAt(i); + buffer.append(key); + buffer.append('='); + Object value = valueAt(i); + if (value != this) { + buffer.append(value); + } else { + buffer.append("(this Map)"); + } + } + buffer.append('}'); + return buffer.toString(); + } + + static int idealByteArraySize(int need) { + for (int i = 4; i < 32; i++) + if (need <= (1 << i) - 12) + return (1 << i) - 12; + + return need; + } + + static int idealBooleanArraySize(int need) { + return idealByteArraySize(need); + } + + static int idealShortArraySize(int need) { + return idealByteArraySize(need * 2) / 2; + } + + static int idealCharArraySize(int need) { + return idealByteArraySize(need * 2) / 2; + } + + static int idealIntArraySize(int need) { + return idealByteArraySize(need * 4) / 4; + } + + static int idealFloatArraySize(int need) { + return idealByteArraySize(need * 4) / 4; + } + + static int idealObjectArraySize(int need) { + return idealByteArraySize(need * 4) / 4; + } + + static int idealLongArraySize(int need) { + return idealByteArraySize(need * 8) / 8; + } + + static class ContainerHelpers { + static final boolean[] EMPTY_BOOLEANS = new boolean[0]; + static final int[] EMPTY_INTS = new int[0]; + static final long[] EMPTY_LONGS = new long[0]; + static final Object[] EMPTY_OBJECTS = new Object[0]; + + // This is Arrays.binarySearch(), but doesn't do any argument validation. + static int binarySearch(int[] array, int size, int value) { + int lo = 0; + int hi = size - 1; + + while (lo <= hi) { + final int mid = (lo + hi) >>> 1; + final int midVal = array[mid]; + + if (midVal < value) { + lo = mid + 1; + } else if (midVal > value) { + hi = mid - 1; + } else { + return mid; // value found + } + } + return ~lo; // value not present + } + } + +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/RecyclerView.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/RecyclerView.java new file mode 100644 index 000000000..c5a0fe725 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/RecyclerView.java @@ -0,0 +1,9321 @@ +/* + * Copyright (C) 2013 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.android.support.widget; + +import android.content.Context; +import android.database.Observable; +import android.graphics.Canvas; +import android.graphics.PointF; +import android.graphics.Rect; +import android.os.Build; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.Nullable; +import android.support.v4.util.ArrayMap; +import android.support.v4.view.InputDeviceCompat; +import android.support.v4.view.MotionEventCompat; +import android.support.v4.view.ScrollingView; +import android.support.v4.view.VelocityTrackerCompat; +import android.support.v4.view.ViewCompat; +import android.support.v4.view.ViewConfigurationCompat; +import android.support.v4.view.accessibility.AccessibilityEventCompat; +import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; +import android.support.v4.view.accessibility.AccessibilityRecordCompat; +import android.support.v4.widget.EdgeEffectCompat; +import android.support.v4.widget.ScrollerCompat; +import static org.telegram.android.support.widget.AdapterHelper.UpdateOp; +import static org.telegram.android.support.widget.AdapterHelper.Callback; + +import android.util.AttributeSet; +import android.util.Log; +import android.util.SparseArray; +import android.util.SparseIntArray; +import android.util.TypedValue; +import android.view.FocusFinder; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.ViewParent; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; +import android.view.animation.Interpolator; + +import org.telegram.android.AndroidUtilities; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * A flexible view for providing a limited window into a large data set. + * + *

Glossary of terms:

+ * + *
    + *
  • Adapter: A subclass of {@link Adapter} responsible for providing views + * that represent items in a data set.
  • + *
  • Position: The position of a data item within an Adapter.
  • + *
  • Index: The index of an attached child view as used in a call to + * {@link ViewGroup#getChildAt}. Contrast with Position.
  • + *
  • Binding: The process of preparing a child view to display data corresponding + * to a position within the adapter.
  • + *
  • Recycle (view): A view previously used to display data for a specific adapter + * position may be placed in a cache for later reuse to display the same type of data again + * later. This can drastically improve performance by skipping initial layout inflation + * or construction.
  • + *
  • Scrap (view): A child view that has entered into a temporarily detached + * state during layout. Scrap views may be reused without becoming fully detached + * from the parent RecyclerView, either unmodified if no rebinding is required or modified + * by the adapter if the view was considered dirty.
  • + *
  • Dirty (view): A child view that must be rebound by the adapter before + * being displayed.
  • + *
+ * + *

Positions in RecyclerView:

+ *

+ * RecyclerView introduces an additional level of abstraction between the {@link Adapter} and + * {@link LayoutManager} to be able to detect data set changes in batches during a layout + * calculation. This saves LayoutManager from tracking adapter changes to calculate animations. + * It also helps with performance because all view bindings happen at the same time and unnecessary + * bindings are avoided. + *

+ * For this reason, there are two types of position related methods in RecyclerView: + *

    + *
  • layout position: Position of an item in the latest layout calculation. This is the + * position from the LayoutManager's perspective.
  • + *
  • adapter position: Position of an item in the adapter. This is the position from + * the Adapter's perspective.
  • + *
+ *

+ * These two positions are the same except the time between dispatching adapter.notify* + * events and calculating the updated layout. + *

+ * Methods that return or receive *LayoutPosition* use position as of the latest + * layout calculation (e.g. {@link ViewHolder#getLayoutPosition()}, + * {@link #findViewHolderForLayoutPosition(int)}). These positions include all changes until the + * last layout calculation. You can rely on these positions to be consistent with what user is + * currently seeing on the screen. For example, if you have a list of items on the screen and user + * asks for the 5th element, you should use these methods as they'll match what user + * is seeing. + *

+ * The other set of position related methods are in the form of + * *AdapterPosition*. (e.g. {@link ViewHolder#getAdapterPosition()}, + * {@link #findViewHolderForAdapterPosition(int)}) You should use these methods when you need to + * work with up-to-date adapter positions even if they may not have been reflected to layout yet. + * For example, if you want to access the item in the adapter on a ViewHolder click, you should use + * {@link ViewHolder#getAdapterPosition()}. Beware that these methods may not be able to calculate + * adapter positions if {@link Adapter#notifyDataSetChanged()} has been called and new layout has + * not yet been calculated. For this reasons, you should carefully handle {@link #NO_POSITION} or + * null results from these methods. + *

+ * When writing a {@link LayoutManager} you almost always want to use layout positions whereas when + * writing an {@link Adapter}, you probably want to use adapter positions. + */ +public class RecyclerView extends ViewGroup implements ScrollingView { + + private static final String TAG = "RecyclerView"; + + private static final boolean DEBUG = false; + + /** + * On Kitkat and JB MR2, there is a bug which prevents DisplayList from being invalidated if + * a View is two levels deep(wrt to ViewHolder.itemView). DisplayList can be invalidated by + * setting View's visibility to INVISIBLE when View is detached. On Kitkat and JB MR2, Recycler + * recursively traverses itemView and invalidates display list for each ViewGroup that matches + * this criteria. + */ + private static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 18 + || Build.VERSION.SDK_INT == 19 || Build.VERSION.SDK_INT == 20; + + private static final boolean DISPATCH_TEMP_DETACH = false; + public static final int HORIZONTAL = 0; + public static final int VERTICAL = 1; + + public static final int NO_POSITION = -1; + public static final long NO_ID = -1; + public static final int INVALID_TYPE = -1; + + /** + * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates + * that the RecyclerView should use the standard touch slop for smooth, + * continuous scrolling. + */ + public static final int TOUCH_SLOP_DEFAULT = 0; + + /** + * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates + * that the RecyclerView should use the standard touch slop for scrolling + * widgets that snap to a page or other coarse-grained barrier. + */ + public static final int TOUCH_SLOP_PAGING = 1; + + private static final int MAX_SCROLL_DURATION = 2000; + + /** + * RecyclerView is calculating a scroll. + * If there are too many of these in Systrace, some Views inside RecyclerView might be causing + * it. Try to avoid using EditText, focusable views or handle them with care. + */ + private static final String TRACE_SCROLL_TAG = "RV Scroll"; + + /** + * OnLayout has been called by the View system. + * If this shows up too many times in Systrace, make sure the children of RecyclerView do not + * update themselves directly. This will cause a full re-layout but when it happens via the + * Adapter notifyItemChanged, RecyclerView can avoid full layout calculation. + */ + private static final String TRACE_ON_LAYOUT_TAG = "RV OnLayout"; + + /** + * NotifyDataSetChanged or equal has been called. + * If this is taking a long time, try sending granular notify adapter changes instead of just + * calling notifyDataSetChanged or setAdapter / swapAdapter. Adding stable ids to your adapter + * might help. + */ + private static final String TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG = "RV FullInvalidate"; + + /** + * RecyclerView is doing a layout for partial adapter updates (we know what has changed) + * If this is taking a long time, you may have dispatched too many Adapter updates causing too + * many Views being rebind. Make sure all are necessary and also prefer using notify*Range + * methods. + */ + private static final String TRACE_HANDLE_ADAPTER_UPDATES_TAG = "RV PartialInvalidate"; + + /** + * RecyclerView is rebinding a View. + * If this is taking a lot of time, consider optimizing your layout or make sure you are not + * doing extra operations in onBindViewHolder call. + */ + private static final String TRACE_BIND_VIEW_TAG = "RV OnBindView"; + + /** + * RecyclerView is creating a new View. + * If too many of these present in Systrace: + * - There might be a problem in Recycling (e.g. custom Animations that set transient state and + * prevent recycling or ItemAnimator not implementing the contract properly. ({@link + * > Adapter#onFailedToRecycleView(ViewHolder)}) + * + * - There might be too many item view types. + * > Try merging them + * + * - There might be too many itemChange animations and not enough space in RecyclerPool. + * >Try increasing your pool size and item cache size. + */ + private static final String TRACE_CREATE_VIEW_TAG = "RV CreateView"; + + private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver(); + + final Recycler mRecycler = new Recycler(); + + private SavedState mPendingSavedState; + + AdapterHelper mAdapterHelper; + + ChildHelper mChildHelper; + + /** + * Prior to L, there is no way to query this variable which is why we override the setter and + * track it here. + */ + private boolean mClipToPadding; + + /** + * Note: this Runnable is only ever posted if: + * 1) We've been through first layout + * 2) We know we have a fixed size (mHasFixedSize) + * 3) We're attached + */ + private final Runnable mUpdateChildViewsRunnable = new Runnable() { + public void run() { + if (!mFirstLayoutComplete) { + // a layout request will happen, we should not do layout here. + return; + } + if (mDataSetHasChangedAfterLayout) { + dispatchLayout(); + } else if (mAdapterHelper.hasPendingUpdates()) { + eatRequestLayout(); + mAdapterHelper.preProcess(); + if (!mLayoutRequestEaten) { + // We run this after pre-processing is complete so that ViewHolders have their + // final adapter positions. No need to run it if a layout is already requested. + rebindUpdatedViewHolders(); + } + resumeRequestLayout(true); + } + } + }; + + private final Rect mTempRect = new Rect(); + private Adapter mAdapter; + private LayoutManager mLayout; + private RecyclerListener mRecyclerListener; + private final ArrayList mItemDecorations = new ArrayList(); + private final ArrayList mOnItemTouchListeners = + new ArrayList(); + private OnItemTouchListener mActiveOnItemTouchListener; + private boolean mIsAttached; + private boolean mHasFixedSize; + private boolean mFirstLayoutComplete; + private boolean mEatRequestLayout; + private boolean mLayoutRequestEaten; + // binary OR of change events that were eaten during a layout or scroll. + private int mEatenAccessibilityChangeFlags; + private boolean mAdapterUpdateDuringMeasure; + private final boolean mPostUpdatesOnAnimation; + private final AccessibilityManager mAccessibilityManager; + + /** + * Set to true when an adapter data set changed notification is received. + * In that case, we cannot run any animations since we don't know what happened. + */ + private boolean mDataSetHasChangedAfterLayout = false; + + /** + * This variable is incremented during a dispatchLayout and/or scroll. + * Some methods should not be called during these periods (e.g. adapter data change). + * Doing so will create hard to find bugs so we better check it and throw an exception. + * + * @see #assertInLayoutOrScroll(String) + * @see #assertNotInLayoutOrScroll(String) + */ + private int mLayoutOrScrollCounter = 0; + + private EdgeEffectCompat mLeftGlow, mTopGlow, mRightGlow, mBottomGlow; + + ItemAnimator mItemAnimator = new DefaultItemAnimator(); + + private static final int INVALID_POINTER = -1; + + /** + * The RecyclerView is not currently scrolling. + * @see #getScrollState() + */ + public static final int SCROLL_STATE_IDLE = 0; + + /** + * The RecyclerView is currently being dragged by outside input such as user touch input. + * @see #getScrollState() + */ + public static final int SCROLL_STATE_DRAGGING = 1; + + /** + * The RecyclerView is currently animating to a final position while not under + * outside control. + * @see #getScrollState() + */ + public static final int SCROLL_STATE_SETTLING = 2; + + // Touch/scrolling handling + + private int mScrollState = SCROLL_STATE_IDLE; + private int mScrollPointerId = INVALID_POINTER; + private VelocityTracker mVelocityTracker; + private int mInitialTouchX; + private int mInitialTouchY; + private int mLastTouchX; + private int mLastTouchY; + private int mTouchSlop; + private final int mMinFlingVelocity; + private final int mMaxFlingVelocity; + // This value is used when handling generic motion events. + private float mScrollFactor = Float.MIN_VALUE; + + private final ViewFlinger mViewFlinger = new ViewFlinger(); + + final State mState = new State(); + + private OnScrollListener mScrollListener; + private List mScrollListeners; + + // For use in item animations + boolean mItemsAddedOrRemoved = false; + boolean mItemsChanged = false; + private ItemAnimator.ItemAnimatorListener mItemAnimatorListener = + new ItemAnimatorRestoreListener(); + private boolean mPostedAnimatorRunner = false; + private RecyclerViewAccessibilityDelegate mAccessibilityDelegate; + + // simple array to keep min and max child position during a layout calculation + // preserved not to create a new one in each layout pass + private final int[] mMinMaxLayoutPositions = new int[2]; + + private Runnable mItemAnimatorRunner = new Runnable() { + @Override + public void run() { + if (mItemAnimator != null) { + mItemAnimator.runPendingAnimations(); + } + mPostedAnimatorRunner = false; + } + }; + + private static final Interpolator sQuinticInterpolator = new Interpolator() { + public float getInterpolation(float t) { + t -= 1.0f; + return t * t * t * t * t + 1.0f; + } + }; + + public RecyclerView(Context context) { + this(context, null); + } + + public RecyclerView(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setFocusableInTouchMode(true); + final int version = Build.VERSION.SDK_INT; + mPostUpdatesOnAnimation = version >= 16; + + final ViewConfiguration vc = ViewConfiguration.get(context); + mTouchSlop = vc.getScaledTouchSlop(); + mMinFlingVelocity = vc.getScaledMinimumFlingVelocity(); + mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity(); + setWillNotDraw(ViewCompat.getOverScrollMode(this) == ViewCompat.OVER_SCROLL_NEVER); + + mItemAnimator.setListener(mItemAnimatorListener); + initAdapterManager(); + initChildrenHelper(); + // If not explicitly specified this view is important for accessibility. + if (ViewCompat.getImportantForAccessibility(this) + == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { + ViewCompat.setImportantForAccessibility(this, + ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES); + } + mAccessibilityManager = (AccessibilityManager) getContext() + .getSystemService(Context.ACCESSIBILITY_SERVICE); + setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this)); + } + + /** + * Returns the accessibility delegate compatibility implementation used by the RecyclerView. + * @return An instance of AccessibilityDelegateCompat used by RecyclerView + */ + public RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate() { + return mAccessibilityDelegate; + } + + /** + * Sets the accessibility delegate compatibility implementation used by RecyclerView. + * @param accessibilityDelegate The accessibility delegate to be used by RecyclerView. + */ + public void setAccessibilityDelegateCompat( + RecyclerViewAccessibilityDelegate accessibilityDelegate) { + mAccessibilityDelegate = accessibilityDelegate; + ViewCompat.setAccessibilityDelegate(this, mAccessibilityDelegate); + } + + private void initChildrenHelper() { + mChildHelper = new ChildHelper(new ChildHelper.Callback() { + @Override + public int getChildCount() { + return RecyclerView.this.getChildCount(); + } + + @Override + public void addView(View child, int index) { + RecyclerView.this.addView(child, index); + dispatchChildAttached(child); + } + + @Override + public int indexOfChild(View view) { + return RecyclerView.this.indexOfChild(view); + } + + @Override + public void removeViewAt(int index) { + final View child = RecyclerView.this.getChildAt(index); + if (child != null) { + dispatchChildDetached(child); + } + RecyclerView.this.removeViewAt(index); + } + + @Override + public View getChildAt(int offset) { + return RecyclerView.this.getChildAt(offset); + } + + @Override + public void removeAllViews() { + final int count = getChildCount(); + for (int i = 0; i < count; i ++) { + dispatchChildDetached(getChildAt(i)); + } + RecyclerView.this.removeAllViews(); + } + + @Override + public ViewHolder getChildViewHolder(View view) { + return getChildViewHolderInt(view); + } + + @Override + public void attachViewToParent(View child, int index, + ViewGroup.LayoutParams layoutParams) { + final ViewHolder vh = getChildViewHolderInt(child); + if (vh != null) { + if (!vh.isTmpDetached() && !vh.shouldIgnore()) { + throw new IllegalArgumentException("Called attach on a child which is not" + + " detached: " + vh); + } + if (DEBUG) { + Log.d(TAG, "reAttach " + vh); + } + vh.clearTmpDetachFlag(); + } + RecyclerView.this.attachViewToParent(child, index, layoutParams); + } + + @Override + public void detachViewFromParent(int offset) { + final View view = getChildAt(offset); + if (view != null) { + final ViewHolder vh = getChildViewHolderInt(view); + if (vh != null) { + if (vh.isTmpDetached() && !vh.shouldIgnore()) { + throw new IllegalArgumentException("called detach on an already" + + " detached child " + vh); + } + if (DEBUG) { + Log.d(TAG, "tmpDetach " + vh); + } + vh.addFlags(ViewHolder.FLAG_TMP_DETACHED); + } + } + RecyclerView.this.detachViewFromParent(offset); + } + }); + } + + void initAdapterManager() { + mAdapterHelper = new AdapterHelper(new Callback() { + @Override + public ViewHolder findViewHolder(int position) { + final ViewHolder vh = findViewHolderForPosition(position, true); + if (vh == null) { + return null; + } + // ensure it is not hidden because for adapter helper, the only thing matter is that + // LM thinks view is a child. + if (mChildHelper.isHidden(vh.itemView)) { + if (DEBUG) { + Log.d(TAG, "assuming view holder cannot be find because it is hidden"); + } + return null; + } + return vh; + } + + @Override + public void offsetPositionsForRemovingInvisible(int start, int count) { + offsetPositionRecordsForRemove(start, count, true); + mItemsAddedOrRemoved = true; + mState.mDeletedInvisibleItemCountSincePreviousLayout += count; + } + + @Override + public void offsetPositionsForRemovingLaidOutOrNewView(int positionStart, int itemCount) { + offsetPositionRecordsForRemove(positionStart, itemCount, false); + mItemsAddedOrRemoved = true; + } + + @Override + public void markViewHoldersUpdated(int positionStart, int itemCount) { + viewRangeUpdate(positionStart, itemCount); + mItemsChanged = true; + } + + @Override + public void onDispatchFirstPass(UpdateOp op) { + dispatchUpdate(op); + } + + void dispatchUpdate(UpdateOp op) { + switch (op.cmd) { + case UpdateOp.ADD: + mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount); + break; + case UpdateOp.REMOVE: + mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount); + break; + case UpdateOp.UPDATE: + mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount); + break; + case UpdateOp.MOVE: + mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1); + break; + } + } + + @Override + public void onDispatchSecondPass(UpdateOp op) { + dispatchUpdate(op); + } + + @Override + public void offsetPositionsForAdd(int positionStart, int itemCount) { + offsetPositionRecordsForInsert(positionStart, itemCount); + mItemsAddedOrRemoved = true; + } + + @Override + public void offsetPositionsForMove(int from, int to) { + offsetPositionRecordsForMove(from, to); + // should we create mItemsMoved ? + mItemsAddedOrRemoved = true; + } + }); + } + + /** + * RecyclerView can perform several optimizations if it can know in advance that changes in + * adapter content cannot change the size of the RecyclerView itself. + * If your use of RecyclerView falls into this category, set this to true. + * + * @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView. + */ + public void setHasFixedSize(boolean hasFixedSize) { + mHasFixedSize = hasFixedSize; + } + + /** + * @return true if the app has specified that changes in adapter content cannot change + * the size of the RecyclerView itself. + */ + public boolean hasFixedSize() { + return mHasFixedSize; + } + + @Override + public void setClipToPadding(boolean clipToPadding) { + if (clipToPadding != mClipToPadding) { + invalidateGlows(); + } + mClipToPadding = clipToPadding; + super.setClipToPadding(clipToPadding); + if (mFirstLayoutComplete) { + requestLayout(); + } + } + + /** + * Configure the scrolling touch slop for a specific use case. + * + * Set up the RecyclerView's scrolling motion threshold based on common usages. + * Valid arguments are {@link #TOUCH_SLOP_DEFAULT} and {@link #TOUCH_SLOP_PAGING}. + * + * @param slopConstant One of the TOUCH_SLOP_ constants representing + * the intended usage of this RecyclerView + */ + public void setScrollingTouchSlop(int slopConstant) { + final ViewConfiguration vc = ViewConfiguration.get(getContext()); + switch (slopConstant) { + default: + Log.w(TAG, "setScrollingTouchSlop(): bad argument constant " + + slopConstant + "; using default value"); + // fall-through + case TOUCH_SLOP_DEFAULT: + mTouchSlop = vc.getScaledTouchSlop(); + break; + + case TOUCH_SLOP_PAGING: + mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(vc); + break; + } + } + + /** + * Swaps the current adapter with the provided one. It is similar to + * {@link #setAdapter(Adapter)} but assumes existing adapter and the new adapter uses the same + * {@link ViewHolder} and does not clear the RecycledViewPool. + *

+ * Note that it still calls onAdapterChanged callbacks. + * + * @param adapter The new adapter to set, or null to set no adapter. + * @param removeAndRecycleExistingViews If set to true, RecyclerView will recycle all existing + * Views. If adapters have stable ids and/or you want to + * animate the disappearing views, you may prefer to set + * this to false. + * @see #setAdapter(Adapter) + */ + public void swapAdapter(Adapter adapter, boolean removeAndRecycleExistingViews) { + setAdapterInternal(adapter, true, removeAndRecycleExistingViews); + setDataSetChangedAfterLayout(); + requestLayout(); + } + /** + * Set a new adapter to provide child views on demand. + *

+ * When adapter is changed, all existing views are recycled back to the pool. If the pool has + * only one adapter, it will be cleared. + * + * @param adapter The new adapter to set, or null to set no adapter. + * @see #swapAdapter(Adapter, boolean) + */ + public void setAdapter(Adapter adapter) { + setAdapterInternal(adapter, false, true); + requestLayout(); + } + + /** + * Replaces the current adapter with the new one and triggers listeners. + * @param adapter The new adapter + * @param compatibleWithPrevious If true, the new adapter is using the same View Holders and + * item types with the current adapter (helps us avoid cache + * invalidation). + * @param removeAndRecycleViews If true, we'll remove and recycle all existing views. If + * compatibleWithPrevious is false, this parameter is ignored. + */ + private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious, + boolean removeAndRecycleViews) { + if (mAdapter != null) { + mAdapter.unregisterAdapterDataObserver(mObserver); + mAdapter.onDetachedFromRecyclerView(this); + } + if (!compatibleWithPrevious || removeAndRecycleViews) { + // end all running animations + if (mItemAnimator != null) { + mItemAnimator.endAnimations(); + } + // Since animations are ended, mLayout.children should be equal to + // recyclerView.children. This may not be true if item animator's end does not work as + // expected. (e.g. not release children instantly). It is safer to use mLayout's child + // count. + if (mLayout != null) { + mLayout.removeAndRecycleAllViews(mRecycler); + mLayout.removeAndRecycleScrapInt(mRecycler); + } + // we should clear it here before adapters are swapped to ensure correct callbacks. + mRecycler.clear(); + } + mAdapterHelper.reset(); + final Adapter oldAdapter = mAdapter; + mAdapter = adapter; + if (adapter != null) { + adapter.registerAdapterDataObserver(mObserver); + adapter.onAttachedToRecyclerView(this); + } + if (mLayout != null) { + mLayout.onAdapterChanged(oldAdapter, mAdapter); + } + mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious); + mState.mStructureChanged = true; + markKnownViewsInvalid(); + } + + /** + * Retrieves the previously set adapter or null if no adapter is set. + * + * @return The previously set adapter + * @see #setAdapter(Adapter) + */ + public Adapter getAdapter() { + return mAdapter; + } + + /** + * Register a listener that will be notified whenever a child view is recycled. + * + *

This listener will be called when a LayoutManager or the RecyclerView decides + * that a child view is no longer needed. If an application associates expensive + * or heavyweight data with item views, this may be a good place to release + * or free those resources.

+ * + * @param listener Listener to register, or null to clear + */ + public void setRecyclerListener(RecyclerListener listener) { + mRecyclerListener = listener; + } + + /** + *

Return the offset of the RecyclerView's text baseline from the its top + * boundary. If the LayoutManager of this RecyclerView does not support baseline alignment, + * this method returns -1.

+ * + * @return the offset of the baseline within the RecyclerView's bounds or -1 + * if baseline alignment is not supported + */ + @Override + public int getBaseline() { + if (mLayout != null) { + return mLayout.getBaseline(); + } else { + return super.getBaseline(); + } + } + + /** + * Set the {@link LayoutManager} that this RecyclerView will use. + * + *

In contrast to other adapter-backed views such as {@link android.widget.ListView} + * or {@link android.widget.GridView}, RecyclerView allows client code to provide custom + * layout arrangements for child views. These arrangements are controlled by the + * {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.

+ * + *

Several default strategies are provided for common uses such as lists and grids.

+ * + * @param layout LayoutManager to use + */ + public void setLayoutManager(LayoutManager layout) { + if (layout == mLayout) { + return; + } + // TODO We should do this switch a dispachLayout pass and animate children. There is a good + // chance that LayoutManagers will re-use views. + if (mLayout != null) { + if (mIsAttached) { + mLayout.dispatchDetachedFromWindow(this, mRecycler); + } + mLayout.setRecyclerView(null); + } + mRecycler.clear(); + mChildHelper.removeAllViewsUnfiltered(); + mLayout = layout; + if (layout != null) { + if (layout.mRecyclerView != null) { + throw new IllegalArgumentException("LayoutManager " + layout + + " is already attached to a RecyclerView: " + layout.mRecyclerView); + } + mLayout.setRecyclerView(this); + if (mIsAttached) { + mLayout.dispatchAttachedToWindow(this); + } + } + requestLayout(); + } + + @Override + protected Parcelable onSaveInstanceState() { + SavedState state = new SavedState(super.onSaveInstanceState()); + if (mPendingSavedState != null) { + state.copyFrom(mPendingSavedState); + } else if (mLayout != null) { + state.mLayoutState = mLayout.onSaveInstanceState(); + } else { + state.mLayoutState = null; + } + + return state; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + mPendingSavedState = (SavedState) state; + super.onRestoreInstanceState(mPendingSavedState.getSuperState()); + if (mLayout != null && mPendingSavedState.mLayoutState != null) { + mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState); + } + } + + /** + * Override to prevent freezing of any views created by the adapter. + */ + @Override + protected void dispatchSaveInstanceState(SparseArray container) { + dispatchFreezeSelfOnly(container); + } + + /** + * Override to prevent thawing of any views created by the adapter. + */ + @Override + protected void dispatchRestoreInstanceState(SparseArray container) { + dispatchThawSelfOnly(container); + } + + /** + * Adds a view to the animatingViews list. + * mAnimatingViews holds the child views that are currently being kept around + * purely for the purpose of being animated out of view. They are drawn as a regular + * part of the child list of the RecyclerView, but they are invisible to the LayoutManager + * as they are managed separately from the regular child views. + * @param viewHolder The ViewHolder to be removed + */ + private void addAnimatingView(ViewHolder viewHolder) { + final View view = viewHolder.itemView; + final boolean alreadyParented = view.getParent() == this; + mRecycler.unscrapView(getChildViewHolder(view)); + if (viewHolder.isTmpDetached()) { + // re-attach + mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true); + } else if(!alreadyParented) { + mChildHelper.addView(view, true); + } else { + mChildHelper.hide(view); + } + } + + /** + * Removes a view from the animatingViews list. + * @param view The view to be removed + * @see #addAnimatingView(RecyclerView.ViewHolder) + * @return true if an animating view is removed + */ + private boolean removeAnimatingView(View view) { + eatRequestLayout(); + final boolean removed = mChildHelper.removeViewIfHidden(view); + if (removed) { + final ViewHolder viewHolder = getChildViewHolderInt(view); + mRecycler.unscrapView(viewHolder); + mRecycler.recycleViewHolderInternal(viewHolder); + if (DEBUG) { + Log.d(TAG, "after removing animated view: " + view + ", " + this); + } + } + resumeRequestLayout(false); + return removed; + } + + /** + * Return the {@link LayoutManager} currently responsible for + * layout policy for this RecyclerView. + * + * @return The currently bound LayoutManager + */ + public LayoutManager getLayoutManager() { + return mLayout; + } + + /** + * Retrieve this RecyclerView's {@link RecycledViewPool}. This method will never return null; + * if no pool is set for this view a new one will be created. See + * {@link #setRecycledViewPool(RecycledViewPool) setRecycledViewPool} for more information. + * + * @return The pool used to store recycled item views for reuse. + * @see #setRecycledViewPool(RecycledViewPool) + */ + public RecycledViewPool getRecycledViewPool() { + return mRecycler.getRecycledViewPool(); + } + + /** + * Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views. + * This can be useful if you have multiple RecyclerViews with adapters that use the same + * view types, for example if you have several data sets with the same kinds of item views + * displayed by a {@link android.support.v4.view.ViewPager ViewPager}. + * + * @param pool Pool to set. If this parameter is null a new pool will be created and used. + */ + public void setRecycledViewPool(RecycledViewPool pool) { + mRecycler.setRecycledViewPool(pool); + } + + /** + * Sets a new {@link ViewCacheExtension} to be used by the Recycler. + * + * @param extension ViewCacheExtension to be used or null if you want to clear the existing one. + * + * @see {@link ViewCacheExtension#getViewForPositionAndType(Recycler, int, int)} + */ + public void setViewCacheExtension(ViewCacheExtension extension) { + mRecycler.setViewCacheExtension(extension); + } + + /** + * Set the number of offscreen views to retain before adding them to the potentially shared + * {@link #getRecycledViewPool() recycled view pool}. + * + *

The offscreen view cache stays aware of changes in the attached adapter, allowing + * a LayoutManager to reuse those views unmodified without needing to return to the adapter + * to rebind them.

+ * + * @param size Number of views to cache offscreen before returning them to the general + * recycled view pool + */ + public void setItemViewCacheSize(int size) { + mRecycler.setViewCacheSize(size); + } + + /** + * Return the current scrolling state of the RecyclerView. + * + * @return {@link #SCROLL_STATE_IDLE}, {@link #SCROLL_STATE_DRAGGING} or + * {@link #SCROLL_STATE_SETTLING} + */ + public int getScrollState() { + return mScrollState; + } + + private void setScrollState(int state) { + if (state == mScrollState) { + return; + } + if (DEBUG) { + Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState, + new Exception()); + } + mScrollState = state; + if (state != SCROLL_STATE_SETTLING) { + stopScrollersInternal(); + } + dispatchOnScrollStateChanged(state); + } + + /** + * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can + * affect both measurement and drawing of individual item views. + * + *

Item decorations are ordered. Decorations placed earlier in the list will + * be run/queried/drawn first for their effects on item views. Padding added to views + * will be nested; a padding added by an earlier decoration will mean further + * item decorations in the list will be asked to draw/pad within the previous decoration's + * given area.

+ * + * @param decor Decoration to add + * @param index Position in the decoration chain to insert this decoration at. If this value + * is negative the decoration will be added at the end. + */ + public void addItemDecoration(ItemDecoration decor, int index) { + if (mLayout != null) { + mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll or" + + " layout"); + } + if (mItemDecorations.isEmpty()) { + setWillNotDraw(false); + } + if (index < 0) { + mItemDecorations.add(decor); + } else { + mItemDecorations.add(index, decor); + } + markItemDecorInsetsDirty(); + requestLayout(); + } + + /** + * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can + * affect both measurement and drawing of individual item views. + * + *

Item decorations are ordered. Decorations placed earlier in the list will + * be run/queried/drawn first for their effects on item views. Padding added to views + * will be nested; a padding added by an earlier decoration will mean further + * item decorations in the list will be asked to draw/pad within the previous decoration's + * given area.

+ * + * @param decor Decoration to add + */ + public void addItemDecoration(ItemDecoration decor) { + addItemDecoration(decor, -1); + } + + /** + * Remove an {@link ItemDecoration} from this RecyclerView. + * + *

The given decoration will no longer impact the measurement and drawing of + * item views.

+ * + * @param decor Decoration to remove + * @see #addItemDecoration(ItemDecoration) + */ + public void removeItemDecoration(ItemDecoration decor) { + if (mLayout != null) { + mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll or" + + " layout"); + } + mItemDecorations.remove(decor); + if (mItemDecorations.isEmpty()) { + setWillNotDraw(ViewCompat.getOverScrollMode(this) == ViewCompat.OVER_SCROLL_NEVER); + } + markItemDecorInsetsDirty(); + requestLayout(); + } + + /** + * Set a listener that will be notified of any changes in scroll state or position. + * + * @param listener Listener to set or null to clear + * + * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} and + * {@link #removeOnScrollListener(OnScrollListener)} + */ + @Deprecated + public void setOnScrollListener(OnScrollListener listener) { + mScrollListener = listener; + } + + /** + * Add a listener that will be notified of any changes in scroll state or position. + * + *

Components that add a listener should take care to remove it when finished. + * Other components that take ownership of a view may call {@link #clearOnScrollListeners()} + * to remove all attached listeners.

+ * + * @param listener listener to set or null to clear + */ + public void addOnScrollListener(OnScrollListener listener) { + if (mScrollListeners == null) { + mScrollListeners = new ArrayList(); + } + mScrollListeners.add(listener); + } + + /** + * Remove a listener that was notified of any changes in scroll state or position. + * + * @param listener listener to set or null to clear + */ + public void removeOnScrollListener(OnScrollListener listener) { + if (mScrollListeners != null) { + mScrollListeners.remove(listener); + } + } + + /** + * Remove all secondary listener that were notified of any changes in scroll state or position. + */ + public void clearOnScrollListeners() { + if (mScrollListeners != null) { + mScrollListeners.clear(); + } + } + + /** + * Convenience method to scroll to a certain position. + * + * RecyclerView does not implement scrolling logic, rather forwards the call to + * {@link android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int)} + * @param position Scroll to this adapter position + * @see android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int) + */ + public void scrollToPosition(int position) { + stopScroll(); + if (mLayout == null) { + Log.e(TAG, "Cannot scroll to position a LayoutManager set. " + + "Call setLayoutManager with a non-null argument."); + return; + } + mLayout.scrollToPosition(position); + awakenScrollBars(); + } + + /** + * Starts a smooth scroll to an adapter position. + *

+ * To support smooth scrolling, you must override + * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a + * {@link SmoothScroller}. + *

+ * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to + * provide a custom smooth scroll logic, override + * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your + * LayoutManager. + * + * @param position The adapter position to scroll to + * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int) + */ + public void smoothScrollToPosition(int position) { + if (mLayout == null) { + Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. " + + "Call setLayoutManager with a non-null argument."); + return; + } + mLayout.smoothScrollToPosition(this, mState, position); + } + + @Override + public void scrollTo(int x, int y) { + throw new UnsupportedOperationException( + "RecyclerView does not support scrolling to an absolute position."); + } + + @Override + public void scrollBy(int x, int y) { + if (mLayout == null) { + Log.e(TAG, "Cannot scroll without a LayoutManager set. " + + "Call setLayoutManager with a non-null argument."); + return; + } + final boolean canScrollHorizontal = mLayout.canScrollHorizontally(); + final boolean canScrollVertical = mLayout.canScrollVertically(); + if (canScrollHorizontal || canScrollVertical) { + scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0, false, 0, 0); + } + } + + /** + * Helper method reflect data changes to the state. + *

+ * Adapter changes during a scroll may trigger a crash because scroll assumes no data change + * but data actually changed. + *

+ * This method consumes all deferred changes to avoid that case. + */ + private void consumePendingUpdateOperations() { + mUpdateChildViewsRunnable.run(); + } + + /** + * Does not perform bounds checking. Used by internal methods that have already validated input. + *

+ * It also reports any unused scroll request to the related EdgeEffect. + * + * @param x The amount of horizontal scroll request + * @param y The amount of vertical scroll request + * @param fromMotionEvent If request is originated from a MotionEvent, this should be set to + * true and motionX/motionY should be provided, false otherwise. + * @param motionX The x coordinate of the MotionEvent which triggered this scroll. Unused if + * fromMotionEvent is false. + * @param motionY The y coordinate of the MotionEvent which triggered this scroll. Unused if + * fromMotionEvent is false. + * + * @return Whether any scroll was consumed in either direction. + */ + boolean scrollByInternal(int x, int y, boolean fromMotionEvent, int motionX, int motionY) { + int overscrollX = 0, overscrollY = 0; + int hresult = 0, vresult = 0; + consumePendingUpdateOperations(); + if (mAdapter != null) { + eatRequestLayout(); + onEnterLayoutOrScroll(); + if (x != 0) { + hresult = mLayout.scrollHorizontallyBy(x, mRecycler, mState); + overscrollX = x - hresult; + } + if (y != 0) { + vresult = mLayout.scrollVerticallyBy(y, mRecycler, mState); + overscrollY = y - vresult; + } + if (supportsChangeAnimations()) { + // Fix up shadow views used by changing animations + int count = mChildHelper.getChildCount(); + for (int i = 0; i < count; i++) { + View view = mChildHelper.getChildAt(i); + ViewHolder holder = getChildViewHolder(view); + if (holder != null && holder.mShadowingHolder != null) { + ViewHolder shadowingHolder = holder.mShadowingHolder; + View shadowingView = shadowingHolder != null ? shadowingHolder.itemView : null; + if (shadowingView != null) { + int left = view.getLeft(); + int top = view.getTop(); + if (left != shadowingView.getLeft() || top != shadowingView.getTop()) { + shadowingView.layout(left, top, + left + shadowingView.getWidth(), + top + shadowingView.getHeight()); + } + } + } + } + } + onExitLayoutOrScroll(); + resumeRequestLayout(false); + } + if (!mItemDecorations.isEmpty()) { + invalidate(); + } + if (ViewCompat.getOverScrollMode(this) != ViewCompat.OVER_SCROLL_NEVER) { + if (fromMotionEvent) { + pullGlows(motionX, overscrollX, motionY, overscrollY); + } + considerReleasingGlowsOnScroll(x, y); + } + if (hresult != 0 || vresult != 0) { + dispatchOnScrolled(hresult, vresult); + } + if (!awakenScrollBars()) { + invalidate(); + } + return hresult != 0 || vresult != 0; + } + + /** + *

Compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal + * range. This value is used to compute the length of the thumb within the scrollbar's track. + *

+ * + *

The range is expressed in arbitrary units that must be the same as the units used by + * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollExtent()}.

+ * + *

Default implementation returns 0.

+ * + *

If you want to support scroll bars, override + * {@link RecyclerView.LayoutManager#computeHorizontalScrollOffset(RecyclerView.State)} in your + * LayoutManager.

+ * + * @return The horizontal offset of the scrollbar's thumb + * @see android.support.v7.widget.RecyclerView.LayoutManager#computeHorizontalScrollOffset + * (RecyclerView.Adapter) + */ + @Override + public int computeHorizontalScrollOffset() { + return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollOffset(mState) + : 0; + } + + /** + *

Compute the horizontal extent of the horizontal scrollbar's thumb within the + * horizontal range. This value is used to compute the length of the thumb within the + * scrollbar's track.

+ * + *

The range is expressed in arbitrary units that must be the same as the units used by + * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollOffset()}.

+ * + *

Default implementation returns 0.

+ * + *

If you want to support scroll bars, override + * {@link RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)} in your + * LayoutManager.

+ * + * @return The horizontal extent of the scrollbar's thumb + * @see RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State) + */ + @Override + public int computeHorizontalScrollExtent() { + return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollExtent(mState) : 0; + } + + /** + *

Compute the horizontal range that the horizontal scrollbar represents.

+ * + *

The range is expressed in arbitrary units that must be the same as the units used by + * {@link #computeHorizontalScrollExtent()} and {@link #computeHorizontalScrollOffset()}.

+ * + *

Default implementation returns 0.

+ * + *

If you want to support scroll bars, override + * {@link RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)} in your + * LayoutManager.

+ * + * @return The total horizontal range represented by the vertical scrollbar + * @see RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State) + */ + @Override + public int computeHorizontalScrollRange() { + return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollRange(mState) : 0; + } + + /** + *

Compute the vertical offset of the vertical scrollbar's thumb within the vertical range. + * This value is used to compute the length of the thumb within the scrollbar's track.

+ * + *

The range is expressed in arbitrary units that must be the same as the units used by + * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.

+ * + *

Default implementation returns 0.

+ * + *

If you want to support scroll bars, override + * {@link RecyclerView.LayoutManager#computeVerticalScrollOffset(RecyclerView.State)} in your + * LayoutManager.

+ * + * @return The vertical offset of the scrollbar's thumb + * @see android.support.v7.widget.RecyclerView.LayoutManager#computeVerticalScrollOffset + * (RecyclerView.Adapter) + */ + @Override + public int computeVerticalScrollOffset() { + return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0; + } + + /** + *

Compute the vertical extent of the vertical scrollbar's thumb within the vertical range. + * This value is used to compute the length of the thumb within the scrollbar's track.

+ * + *

The range is expressed in arbitrary units that must be the same as the units used by + * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollOffset()}.

+ * + *

Default implementation returns 0.

+ * + *

If you want to support scroll bars, override + * {@link RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)} in your + * LayoutManager.

+ * + * @return The vertical extent of the scrollbar's thumb + * @see RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State) + */ + @Override + public int computeVerticalScrollExtent() { + return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0; + } + + /** + *

Compute the vertical range that the vertical scrollbar represents.

+ * + *

The range is expressed in arbitrary units that must be the same as the units used by + * {@link #computeVerticalScrollExtent()} and {@link #computeVerticalScrollOffset()}.

+ * + *

Default implementation returns 0.

+ * + *

If you want to support scroll bars, override + * {@link RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)} in your + * LayoutManager.

+ * + * @return The total vertical range represented by the vertical scrollbar + * @see RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State) + */ + @Override + public int computeVerticalScrollRange() { + return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0; + } + + + void eatRequestLayout() { + if (!mEatRequestLayout) { + mEatRequestLayout = true; + mLayoutRequestEaten = false; + } + } + + void resumeRequestLayout(boolean performLayoutChildren) { + if (mEatRequestLayout) { + if (performLayoutChildren && mLayoutRequestEaten && + mLayout != null && mAdapter != null) { + dispatchLayout(); + } + mEatRequestLayout = false; + mLayoutRequestEaten = false; + } + } + + /** + * Animate a scroll by the given amount of pixels along either axis. + * + * @param dx Pixels to scroll horizontally + * @param dy Pixels to scroll vertically + */ + public void smoothScrollBy(int dx, int dy) { + if (mLayout == null) { + Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. " + + "Call setLayoutManager with a non-null argument."); + return; + } + if (!mLayout.canScrollHorizontally()) { + dx = 0; + } + if (!mLayout.canScrollVertically()) { + dy = 0; + } + if (dx != 0 || dy != 0) { + mViewFlinger.smoothScrollBy(dx, dy); + } + } + + /** + * Begin a standard fling with an initial velocity along each axis in pixels per second. + * If the velocity given is below the system-defined minimum this method will return false + * and no fling will occur. + * + * @param velocityX Initial horizontal velocity in pixels per second + * @param velocityY Initial vertical velocity in pixels per second + * @return true if the fling was started, false if the velocity was too low to fling or + * LayoutManager does not support scrolling in the axis fling is issued. + * + * @see LayoutManager#canScrollVertically() + * @see LayoutManager#canScrollHorizontally() + */ + public boolean fling(int velocityX, int velocityY) { + if (mLayout == null) { + Log.e(TAG, "Cannot fling without a LayoutManager set. " + + "Call setLayoutManager with a non-null argument."); + return false; + } + final boolean canScrollHorizontal = mLayout.canScrollHorizontally(); + final boolean canScrollVertical = mLayout.canScrollVertically(); + if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) { + velocityX = 0; + } + if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) { + velocityY = 0; + } + velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity)); + velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity)); + if (velocityX != 0 || velocityY != 0) { + mViewFlinger.fling(velocityX, velocityY); + return true; + } + return false; + } + + /** + * Stop any current scroll in progress, such as one started by + * {@link #smoothScrollBy(int, int)}, {@link #fling(int, int)} or a touch-initiated fling. + */ + public void stopScroll() { + setScrollState(SCROLL_STATE_IDLE); + stopScrollersInternal(); + } + + /** + * Similar to {@link #stopScroll()} but does not set the state. + */ + private void stopScrollersInternal() { + mViewFlinger.stop(); + if (mLayout != null) { + mLayout.stopSmoothScroller(); + } + } + + /** + * Apply a pull to relevant overscroll glow effects + */ + private void pullGlows(int x, int overscrollX, int y, int overscrollY) { + boolean invalidate = false; + if (overscrollX < 0) { + ensureLeftGlow(); + invalidate = mLeftGlow.onPull(-overscrollX / (float) getWidth(), + 1f - y / (float) getHeight()) || invalidate; + } else if (overscrollX > 0) { + ensureRightGlow(); + invalidate = mRightGlow.onPull(overscrollX / (float) getWidth(), + y / (float) getHeight()) || invalidate; + } + + if (overscrollY < 0) { + ensureTopGlow(); + invalidate = mTopGlow.onPull(-overscrollY / (float) getHeight(), + x / (float) getWidth()) || invalidate; + } else if (overscrollY > 0) { + ensureBottomGlow(); + invalidate = mBottomGlow.onPull(overscrollY / (float) getHeight(), + 1f - x / (float) getWidth()) || invalidate; + } + + if (invalidate || overscrollX != 0 || overscrollY != 0) { + ViewCompat.postInvalidateOnAnimation(this); + } + } + + private void releaseGlows() { + boolean needsInvalidate = false; + if (mLeftGlow != null) needsInvalidate = mLeftGlow.onRelease(); + if (mTopGlow != null) needsInvalidate |= mTopGlow.onRelease(); + if (mRightGlow != null) needsInvalidate |= mRightGlow.onRelease(); + if (mBottomGlow != null) needsInvalidate |= mBottomGlow.onRelease(); + if (needsInvalidate) { + ViewCompat.postInvalidateOnAnimation(this); + } + } + + private void considerReleasingGlowsOnScroll(int dx, int dy) { + boolean needsInvalidate = false; + if (mLeftGlow != null && !mLeftGlow.isFinished() && dx > 0) { + needsInvalidate = mLeftGlow.onRelease(); + } + if (mRightGlow != null && !mRightGlow.isFinished() && dx < 0) { + needsInvalidate |= mRightGlow.onRelease(); + } + if (mTopGlow != null && !mTopGlow.isFinished() && dy > 0) { + needsInvalidate |= mTopGlow.onRelease(); + } + if (mBottomGlow != null && !mBottomGlow.isFinished() && dy < 0) { + needsInvalidate |= mBottomGlow.onRelease(); + } + if (needsInvalidate) { + ViewCompat.postInvalidateOnAnimation(this); + } + } + + void absorbGlows(int velocityX, int velocityY) { + if (velocityX < 0) { + ensureLeftGlow(); + mLeftGlow.onAbsorb(-velocityX); + } else if (velocityX > 0) { + ensureRightGlow(); + mRightGlow.onAbsorb(velocityX); + } + + if (velocityY < 0) { + ensureTopGlow(); + mTopGlow.onAbsorb(-velocityY); + } else if (velocityY > 0) { + ensureBottomGlow(); + mBottomGlow.onAbsorb(velocityY); + } + + if (velocityX != 0 || velocityY != 0) { + ViewCompat.postInvalidateOnAnimation(this); + } + } + + void ensureLeftGlow() { + if (mLeftGlow != null) { + return; + } + mLeftGlow = new EdgeEffectCompat(getContext()); + if (mClipToPadding) { + mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), + getMeasuredWidth() - getPaddingLeft() - getPaddingRight()); + } else { + mLeftGlow.setSize(getMeasuredHeight(), getMeasuredWidth()); + } + } + + void ensureRightGlow() { + if (mRightGlow != null) { + return; + } + mRightGlow = new EdgeEffectCompat(getContext()); + if (mClipToPadding) { + mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), + getMeasuredWidth() - getPaddingLeft() - getPaddingRight()); + } else { + mRightGlow.setSize(getMeasuredHeight(), getMeasuredWidth()); + } + } + + void ensureTopGlow() { + if (mTopGlow != null) { + return; + } + mTopGlow = new EdgeEffectCompat(getContext()); + if (mClipToPadding) { + mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), + getMeasuredHeight() - getPaddingTop() - getPaddingBottom()); + } else { + mTopGlow.setSize(getMeasuredWidth(), getMeasuredHeight()); + } + + } + + void ensureBottomGlow() { + if (mBottomGlow != null) { + return; + } + mBottomGlow = new EdgeEffectCompat(getContext()); + if (mClipToPadding) { + mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), + getMeasuredHeight() - getPaddingTop() - getPaddingBottom()); + } else { + mBottomGlow.setSize(getMeasuredWidth(), getMeasuredHeight()); + } + } + + void invalidateGlows() { + mLeftGlow = mRightGlow = mTopGlow = mBottomGlow = null; + } + + // Focus handling + + @Override + public View focusSearch(View focused, int direction) { + View result = mLayout.onInterceptFocusSearch(focused, direction); + if (result != null) { + return result; + } + final FocusFinder ff = FocusFinder.getInstance(); + result = ff.findNextFocus(this, focused, direction); + if (result == null && mAdapter != null && mLayout != null) { + eatRequestLayout(); + result = mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState); + resumeRequestLayout(false); + } + return result != null ? result : super.focusSearch(focused, direction); + } + + @Override + public void requestChildFocus(View child, View focused) { + if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) { + mTempRect.set(0, 0, focused.getWidth(), focused.getHeight()); + + // get item decor offsets w/o refreshing. If they are invalid, there will be another + // layout pass to fix them, then it is LayoutManager's responsibility to keep focused + // View in viewport. + final ViewGroup.LayoutParams focusedLayoutParams = focused.getLayoutParams(); + if (focusedLayoutParams instanceof LayoutParams) { + // if focused child has item decors, use them. Otherwise, ignore. + final LayoutParams lp = (LayoutParams) focusedLayoutParams; + if (!lp.mInsetsDirty) { + final Rect insets = lp.mDecorInsets; + mTempRect.left -= insets.left; + mTempRect.right += insets.right; + mTempRect.top -= insets.top; + mTempRect.bottom += insets.bottom; + } + } + + offsetDescendantRectToMyCoords(focused, mTempRect); + offsetRectIntoDescendantCoords(child, mTempRect); + requestChildRectangleOnScreen(child, mTempRect, !mFirstLayoutComplete); + } + super.requestChildFocus(child, focused); + } + + @Override + public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) { + return mLayout.requestChildRectangleOnScreen(this, child, rect, immediate); + } + + @Override + public void addFocusables(ArrayList views, int direction, int focusableMode) { + if (mLayout == null || !mLayout.onAddFocusables(this, views, direction, focusableMode)) { + super.addFocusables(views, direction, focusableMode); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mLayoutOrScrollCounter = 0; + mIsAttached = true; + mFirstLayoutComplete = false; + if (mLayout != null) { + mLayout.dispatchAttachedToWindow(this); + } + mPostedAnimatorRunner = false; + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (mItemAnimator != null) { + mItemAnimator.endAnimations(); + } + mFirstLayoutComplete = false; + + stopScroll(); + mIsAttached = false; + if (mLayout != null) { + mLayout.dispatchDetachedFromWindow(this, mRecycler); + } + removeCallbacks(mItemAnimatorRunner); + } + + /** + * Checks if RecyclerView is in the middle of a layout or scroll and throws an + * {@link IllegalStateException} if it is not. + * + * @param message The message for the exception. Can be null. + * @see #assertNotInLayoutOrScroll(String) + */ + void assertInLayoutOrScroll(String message) { + if (!isRunningLayoutOrScroll()) { + if (message == null) { + throw new IllegalStateException("Cannot call this method unless RecyclerView is " + + "computing a layout or scrolling"); + } + throw new IllegalStateException(message); + + } + } + + /** + * Checks if RecyclerView is in the middle of a layout or scroll and throws an + * {@link IllegalStateException} if it is. + * + * @param message The message for the exception. Can be null. + * @see #assertInLayoutOrScroll(String) + */ + void assertNotInLayoutOrScroll(String message) { + if (isRunningLayoutOrScroll()) { + if (message == null) { + throw new IllegalStateException("Cannot call this method while RecyclerView is " + + "computing a layout or scrolling"); + } + throw new IllegalStateException(message); + } + } + + /** + * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched + * to child views or this view's standard scrolling behavior. + * + *

Client code may use listeners to implement item manipulation behavior. Once a listener + * returns true from + * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its + * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called + * for each incoming MotionEvent until the end of the gesture.

+ * + * @param listener Listener to add + */ + public void addOnItemTouchListener(OnItemTouchListener listener) { + mOnItemTouchListeners.add(listener); + } + + /** + * Remove an {@link OnItemTouchListener}. It will no longer be able to intercept touch events. + * + * @param listener Listener to remove + */ + public void removeOnItemTouchListener(OnItemTouchListener listener) { + mOnItemTouchListeners.remove(listener); + if (mActiveOnItemTouchListener == listener) { + mActiveOnItemTouchListener = null; + } + } + + private boolean dispatchOnItemTouchIntercept(MotionEvent e) { + final int action = e.getAction(); + if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) { + mActiveOnItemTouchListener = null; + } + + final int listenerCount = mOnItemTouchListeners.size(); + for (int i = 0; i < listenerCount; i++) { + final OnItemTouchListener listener = mOnItemTouchListeners.get(i); + if (listener.onInterceptTouchEvent(this, e) && action != MotionEvent.ACTION_CANCEL) { + mActiveOnItemTouchListener = listener; + return true; + } + } + return false; + } + + private boolean dispatchOnItemTouch(MotionEvent e) { + final int action = e.getAction(); + if (mActiveOnItemTouchListener != null) { + if (action == MotionEvent.ACTION_DOWN) { + // Stale state from a previous gesture, we're starting a new one. Clear it. + mActiveOnItemTouchListener = null; + } else { + mActiveOnItemTouchListener.onTouchEvent(this, e); + if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { + // Clean up for the next gesture. + mActiveOnItemTouchListener = null; + } + return true; + } + } + + // Listeners will have already received the ACTION_DOWN via dispatchOnItemTouchIntercept + // as called from onInterceptTouchEvent; skip it. + if (action != MotionEvent.ACTION_DOWN) { + final int listenerCount = mOnItemTouchListeners.size(); + for (int i = 0; i < listenerCount; i++) { + final OnItemTouchListener listener = mOnItemTouchListeners.get(i); + if (listener.onInterceptTouchEvent(this, e)) { + mActiveOnItemTouchListener = listener; + return true; + } + } + } + return false; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent e) { + if (dispatchOnItemTouchIntercept(e)) { + cancelTouch(); + return true; + } + + final boolean canScrollHorizontally = mLayout.canScrollHorizontally(); + final boolean canScrollVertically = mLayout.canScrollVertically(); + + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } + mVelocityTracker.addMovement(e); + + final int action = MotionEventCompat.getActionMasked(e); + final int actionIndex = MotionEventCompat.getActionIndex(e); + + switch (action) { + case MotionEvent.ACTION_DOWN: + mScrollPointerId = MotionEventCompat.getPointerId(e, 0); + mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f); + mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f); + + if (mScrollState == SCROLL_STATE_SETTLING) { + getParent().requestDisallowInterceptTouchEvent(true); + setScrollState(SCROLL_STATE_DRAGGING); + } + break; + + case MotionEventCompat.ACTION_POINTER_DOWN: + mScrollPointerId = MotionEventCompat.getPointerId(e, actionIndex); + mInitialTouchX = mLastTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f); + mInitialTouchY = mLastTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f); + break; + + case MotionEvent.ACTION_MOVE: { + final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId); + if (index < 0) { + Log.e(TAG, "Error processing scroll; pointer index for id " + + mScrollPointerId + " not found. Did any MotionEvents get skipped?"); + return false; + } + + final int x = (int) (MotionEventCompat.getX(e, index) + 0.5f); + final int y = (int) (MotionEventCompat.getY(e, index) + 0.5f); + if (mScrollState != SCROLL_STATE_DRAGGING) { + final int dx = x - mInitialTouchX; + final int dy = y - mInitialTouchY; + boolean startScroll = false; + if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) { + mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1); + startScroll = true; + } + if (canScrollVertically && Math.abs(dy) > mTouchSlop) { + mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1); + startScroll = true; + } + if (startScroll) { + setScrollState(SCROLL_STATE_DRAGGING); + } + } + } break; + + case MotionEventCompat.ACTION_POINTER_UP: { + onPointerUp(e); + } break; + + case MotionEvent.ACTION_UP: { + mVelocityTracker.clear(); + } break; + + case MotionEvent.ACTION_CANCEL: { + cancelTouch(); + } + } + return mScrollState == SCROLL_STATE_DRAGGING; + } + + @Override + public boolean onTouchEvent(MotionEvent e) { + if (dispatchOnItemTouch(e)) { + cancelTouch(); + return true; + } + + final boolean canScrollHorizontally = mLayout.canScrollHorizontally(); + final boolean canScrollVertically = mLayout.canScrollVertically(); + + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } + mVelocityTracker.addMovement(e); + + final int action = MotionEventCompat.getActionMasked(e); + final int actionIndex = MotionEventCompat.getActionIndex(e); + + switch (action) { + case MotionEvent.ACTION_DOWN: { + mScrollPointerId = MotionEventCompat.getPointerId(e, 0); + mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f); + mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f); + } break; + + case MotionEventCompat.ACTION_POINTER_DOWN: { + mScrollPointerId = MotionEventCompat.getPointerId(e, actionIndex); + mInitialTouchX = mLastTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f); + mInitialTouchY = mLastTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f); + } break; + + case MotionEvent.ACTION_MOVE: { + final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId); + if (index < 0) { + Log.e(TAG, "Error processing scroll; pointer index for id " + + mScrollPointerId + " not found. Did any MotionEvents get skipped?"); + return false; + } + + final int x = (int) (MotionEventCompat.getX(e, index) + 0.5f); + final int y = (int) (MotionEventCompat.getY(e, index) + 0.5f); + if (mScrollState != SCROLL_STATE_DRAGGING) { + final int dx = x - mInitialTouchX; + final int dy = y - mInitialTouchY; + boolean startScroll = false; + if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) { + mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1); + startScroll = true; + } + if (canScrollVertically && Math.abs(dy) > mTouchSlop) { + mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1); + startScroll = true; + } + if (startScroll) { + setScrollState(SCROLL_STATE_DRAGGING); + } + } + if (mScrollState == SCROLL_STATE_DRAGGING) { + final int dx = x - mLastTouchX; + final int dy = y - mLastTouchY; + if (scrollByInternal(canScrollHorizontally ? -dx : 0, + canScrollVertically ? -dy : 0, true, x, y)) { + getParent().requestDisallowInterceptTouchEvent(true); + } + } + mLastTouchX = x; + mLastTouchY = y; + } break; + + case MotionEventCompat.ACTION_POINTER_UP: { + onPointerUp(e); + } break; + + case MotionEvent.ACTION_UP: { + mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity); + final float xvel = canScrollHorizontally ? + -VelocityTrackerCompat.getXVelocity(mVelocityTracker, mScrollPointerId) : 0; + final float yvel = canScrollVertically ? + -VelocityTrackerCompat.getYVelocity(mVelocityTracker, mScrollPointerId) : 0; + if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) { + setScrollState(SCROLL_STATE_IDLE); + } + mVelocityTracker.clear(); + releaseGlows(); + } break; + + case MotionEvent.ACTION_CANCEL: { + cancelTouch(); + } break; + } + + return true; + } + + private void cancelTouch() { + if (mVelocityTracker != null) { + mVelocityTracker.clear(); + } + releaseGlows(); + setScrollState(SCROLL_STATE_IDLE); + } + + private void onPointerUp(MotionEvent e) { + final int actionIndex = MotionEventCompat.getActionIndex(e); + if (MotionEventCompat.getPointerId(e, actionIndex) == mScrollPointerId) { + // Pick a new pointer to pick up the slack. + final int newIndex = actionIndex == 0 ? 1 : 0; + mScrollPointerId = MotionEventCompat.getPointerId(e, newIndex); + mInitialTouchX = mLastTouchX = (int) (MotionEventCompat.getX(e, newIndex) + 0.5f); + mInitialTouchY = mLastTouchY = (int) (MotionEventCompat.getY(e, newIndex) + 0.5f); + } + } + + // @Override + public boolean onGenericMotionEvent(MotionEvent event) { + if (mLayout == null) { + return false; + } + if ((MotionEventCompat.getSource(event) & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) { + if (event.getAction() == MotionEventCompat.ACTION_SCROLL) { + final float vScroll, hScroll; + if (mLayout.canScrollVertically()) { + vScroll = MotionEventCompat + .getAxisValue(event, MotionEventCompat.AXIS_VSCROLL); + } else { + vScroll = 0f; + } + if (mLayout.canScrollHorizontally()) { + hScroll = MotionEventCompat + .getAxisValue(event, MotionEventCompat.AXIS_HSCROLL); + } else { + hScroll = 0f; + } + + if (vScroll != 0 || hScroll != 0) { + final float scrollFactor = getScrollFactor(); + scrollBy((int) (hScroll * scrollFactor), (int) (vScroll * scrollFactor)); + } + } + } + return false; + } + + /** + * Ported from View.getVerticalScrollFactor. + */ + private float getScrollFactor() { + if (mScrollFactor == Float.MIN_VALUE) { + TypedValue outValue = new TypedValue(); + if (getContext().getTheme().resolveAttribute( + android.R.attr.listPreferredItemHeight, outValue, true)) { + mScrollFactor = outValue.getDimension( + getContext().getResources().getDisplayMetrics()); + } else { + return 0; //listPreferredItemHeight is not defined, no generic scrolling + } + + } + return mScrollFactor; + } + + @Override + protected void onMeasure(int widthSpec, int heightSpec) { + if (mAdapterUpdateDuringMeasure) { + eatRequestLayout(); + processAdapterUpdatesAndSetAnimationFlags(); + + if (mState.mRunPredictiveAnimations) { + // TODO: try to provide a better approach. + // When RV decides to run predictive animations, we need to measure in pre-layout + // state so that pre-layout pass results in correct layout. + // On the other hand, this will prevent the layout manager from resizing properly. + mState.mInPreLayout = true; + } else { + // consume remaining updates to provide a consistent state with the layout pass. + mAdapterHelper.consumeUpdatesInOnePass(); + mState.mInPreLayout = false; + } + mAdapterUpdateDuringMeasure = false; + resumeRequestLayout(false); + } + + if (mAdapter != null) { + mState.mItemCount = mAdapter.getItemCount(); + } else { + mState.mItemCount = 0; + } + if (mLayout == null) { + defaultOnMeasure(widthSpec, heightSpec); + } else { + mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec); + } + + mState.mInPreLayout = false; // clear + } + + /** + * Used when onMeasure is called before layout manager is set + */ + private void defaultOnMeasure(int widthSpec, int heightSpec) { + final int widthMode = MeasureSpec.getMode(widthSpec); + final int heightMode = MeasureSpec.getMode(heightSpec); + final int widthSize = MeasureSpec.getSize(widthSpec); + final int heightSize = MeasureSpec.getSize(heightSpec); + + int width = 0; + int height = 0; + + switch (widthMode) { + case MeasureSpec.EXACTLY: + case MeasureSpec.AT_MOST: + width = widthSize; + break; + case MeasureSpec.UNSPECIFIED: + default: + width = ViewCompat.getMinimumWidth(this); + break; + } + + switch (heightMode) { + case MeasureSpec.EXACTLY: + case MeasureSpec.AT_MOST: + height = heightSize; + break; + case MeasureSpec.UNSPECIFIED: + default: + height = ViewCompat.getMinimumHeight(this); + break; + } + + setMeasuredDimension(width, height); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + if (w != oldw || h != oldh) { + invalidateGlows(); + } + } + + /** + * Sets the {@link ItemAnimator} that will handle animations involving changes + * to the items in this RecyclerView. By default, RecyclerView instantiates and + * uses an instance of {@link DefaultItemAnimator}. Whether item animations are + * enabled for the RecyclerView depends on the ItemAnimator and whether + * the LayoutManager {@link LayoutManager#supportsPredictiveItemAnimations() + * supports item animations}. + * + * @param animator The ItemAnimator being set. If null, no animations will occur + * when changes occur to the items in this RecyclerView. + */ + public void setItemAnimator(ItemAnimator animator) { + if (mItemAnimator != null) { + mItemAnimator.endAnimations(); + mItemAnimator.setListener(null); + } + mItemAnimator = animator; + if (mItemAnimator != null) { + mItemAnimator.setListener(mItemAnimatorListener); + } + } + + private void onEnterLayoutOrScroll() { + mLayoutOrScrollCounter ++; + } + + private void onExitLayoutOrScroll() { + mLayoutOrScrollCounter --; + if (mLayoutOrScrollCounter < 1) { + if (DEBUG && mLayoutOrScrollCounter < 0) { + throw new IllegalStateException("layout or scroll counter cannot go below zero." + + "Some calls are not matching"); + } + mLayoutOrScrollCounter = 0; + dispatchContentChangedIfNecessary(); + } + } + + private void dispatchContentChangedIfNecessary() { + final int flags = mEatenAccessibilityChangeFlags; + mEatenAccessibilityChangeFlags = 0; + if (flags != 0 && mAccessibilityManager != null && mAccessibilityManager.isEnabled()) { + final AccessibilityEvent event = AccessibilityEvent.obtain(); + event.setEventType(AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED); + AccessibilityEventCompat.setContentChangeTypes(event, flags); + sendAccessibilityEventUnchecked(event); + } + } + + boolean isRunningLayoutOrScroll() { + return mLayoutOrScrollCounter > 0; + } + + /** + * Returns true if an accessibility event should not be dispatched now. This happens when an + * accessibility request arrives while RecyclerView does not have a stable state which is very + * hard to handle for a LayoutManager. Instead, this method records necessary information about + * the event and dispatches a window change event after the critical section is finished. + * + * @return True if the accessibility event should be postponed. + */ + boolean shouldDeferAccessibilityEvent(AccessibilityEvent event) { + if (isRunningLayoutOrScroll()) { + int type = 0; + if (event != null) { + type = AccessibilityEventCompat.getContentChangeTypes(event); + } + if (type == 0) { + type = AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED; + } + mEatenAccessibilityChangeFlags |= type; + return true; + } + return false; + } + + @Override + public void sendAccessibilityEventUnchecked(AccessibilityEvent event) { + if (shouldDeferAccessibilityEvent(event)) { + return; + } + super.sendAccessibilityEventUnchecked(event); + } + + /** + * Gets the current ItemAnimator for this RecyclerView. A null return value + * indicates that there is no animator and that item changes will happen without + * any animations. By default, RecyclerView instantiates and + * uses an instance of {@link DefaultItemAnimator}. + * + * @return ItemAnimator The current ItemAnimator. If null, no animations will occur + * when changes occur to the items in this RecyclerView. + */ + public ItemAnimator getItemAnimator() { + return mItemAnimator; + } + + private boolean supportsChangeAnimations() { + return mItemAnimator != null && mItemAnimator.getSupportsChangeAnimations(); + } + + /** + * Post a runnable to the next frame to run pending item animations. Only the first such + * request will be posted, governed by the mPostedAnimatorRunner flag. + */ + private void postAnimationRunner() { + if (!mPostedAnimatorRunner && mIsAttached) { + ViewCompat.postOnAnimation(this, mItemAnimatorRunner); + mPostedAnimatorRunner = true; + } + } + + private boolean predictiveItemAnimationsEnabled() { + return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations()); + } + + /** + * Consumes adapter updates and calculates which type of animations we want to run. + * Called in onMeasure and dispatchLayout. + *

+ * This method may process only the pre-layout state of updates or all of them. + */ + private void processAdapterUpdatesAndSetAnimationFlags() { + if (mDataSetHasChangedAfterLayout) { + // Processing these items have no value since data set changed unexpectedly. + // Instead, we just reset it. + mAdapterHelper.reset(); + markKnownViewsInvalid(); + mLayout.onItemsChanged(this); + } + // simple animations are a subset of advanced animations (which will cause a + // pre-layout step) + // If layout supports predictive animations, pre-process to decide if we want to run them + if (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations()) { + mAdapterHelper.preProcess(); + } else { + mAdapterHelper.consumeUpdatesInOnePass(); + } + boolean animationTypeSupported = (mItemsAddedOrRemoved && !mItemsChanged) || + (mItemsAddedOrRemoved || (mItemsChanged && supportsChangeAnimations())); + mState.mRunSimpleAnimations = mFirstLayoutComplete && mItemAnimator != null && + (mDataSetHasChangedAfterLayout || animationTypeSupported || + mLayout.mRequestedSimpleAnimations) && + (!mDataSetHasChangedAfterLayout || mAdapter.hasStableIds()); + mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations && + animationTypeSupported && !mDataSetHasChangedAfterLayout && + predictiveItemAnimationsEnabled(); + } + + /** + * Wrapper around layoutChildren() that handles animating changes caused by layout. + * Animations work on the assumption that there are five different kinds of items + * in play: + * PERSISTENT: items are visible before and after layout + * REMOVED: items were visible before layout and were removed by the app + * ADDED: items did not exist before layout and were added by the app + * DISAPPEARING: items exist in the data set before/after, but changed from + * visible to non-visible in the process of layout (they were moved off + * screen as a side-effect of other changes) + * APPEARING: items exist in the data set before/after, but changed from + * non-visible to visible in the process of layout (they were moved on + * screen as a side-effect of other changes) + * The overall approach figures out what items exist before/after layout and + * infers one of the five above states for each of the items. Then the animations + * are set up accordingly: + * PERSISTENT views are moved ({@link ItemAnimator#animateMove(ViewHolder, int, int, int, int)}) + * REMOVED views are removed ({@link ItemAnimator#animateRemove(ViewHolder)}) + * ADDED views are added ({@link ItemAnimator#animateAdd(ViewHolder)}) + * DISAPPEARING views are moved off screen + * APPEARING views are moved on screen + */ + void dispatchLayout() { + if (mAdapter == null) { + Log.e(TAG, "No adapter attached; skipping layout"); + return; + } + if (mLayout == null) { + Log.e(TAG, "No layout manager attached; skipping layout"); + return; + } + mState.mDisappearingViewsInLayoutPass.clear(); + eatRequestLayout(); + onEnterLayoutOrScroll(); + + processAdapterUpdatesAndSetAnimationFlags(); + + mState.mOldChangedHolders = mState.mRunSimpleAnimations && mItemsChanged + && supportsChangeAnimations() ? new ArrayMap() : null; + mItemsAddedOrRemoved = mItemsChanged = false; + ArrayMap appearingViewInitialBounds = null; + mState.mInPreLayout = mState.mRunPredictiveAnimations; + mState.mItemCount = mAdapter.getItemCount(); + findMinMaxChildLayoutPositions(mMinMaxLayoutPositions); + + if (mState.mRunSimpleAnimations) { + // Step 0: Find out where all non-removed items are, pre-layout + mState.mPreLayoutHolderMap.clear(); + mState.mPostLayoutHolderMap.clear(); + int count = mChildHelper.getChildCount(); + for (int i = 0; i < count; ++i) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i)); + if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) { + continue; + } + final View view = holder.itemView; + mState.mPreLayoutHolderMap.put(holder, new ItemHolderInfo(holder, + view.getLeft(), view.getTop(), view.getRight(), view.getBottom())); + } + } + if (mState.mRunPredictiveAnimations) { + // Step 1: run prelayout: This will use the old positions of items. The layout manager + // is expected to layout everything, even removed items (though not to add removed + // items back to the container). This gives the pre-layout position of APPEARING views + // which come into existence as part of the real layout. + + // Save old positions so that LayoutManager can run its mapping logic. + saveOldPositions(); + // processAdapterUpdatesAndSetAnimationFlags already run pre-layout animations. + if (mState.mOldChangedHolders != null) { + int count = mChildHelper.getChildCount(); + for (int i = 0; i < count; ++i) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i)); + if (holder.isChanged() && !holder.isRemoved() && !holder.shouldIgnore()) { + long key = getChangedHolderKey(holder); + mState.mOldChangedHolders.put(key, holder); + mState.mPreLayoutHolderMap.remove(holder); + } + } + } + + final boolean didStructureChange = mState.mStructureChanged; + mState.mStructureChanged = false; + // temporarily disable flag because we are asking for previous layout + mLayout.onLayoutChildren(mRecycler, mState); + mState.mStructureChanged = didStructureChange; + + appearingViewInitialBounds = new ArrayMap(); + for (int i = 0; i < mChildHelper.getChildCount(); ++i) { + boolean found = false; + View child = mChildHelper.getChildAt(i); + if (getChildViewHolderInt(child).shouldIgnore()) { + continue; + } + for (int j = 0; j < mState.mPreLayoutHolderMap.size(); ++j) { + ViewHolder holder = mState.mPreLayoutHolderMap.keyAt(j); + if (holder.itemView == child) { + found = true; + break; + } + } + if (!found) { + appearingViewInitialBounds.put(child, new Rect(child.getLeft(), child.getTop(), + child.getRight(), child.getBottom())); + } + } + // we don't process disappearing list because they may re-appear in post layout pass. + clearOldPositions(); + mAdapterHelper.consumePostponedUpdates(); + } else { + clearOldPositions(); + // in case pre layout did run but we decided not to run predictive animations. + mAdapterHelper.consumeUpdatesInOnePass(); + if (mState.mOldChangedHolders != null) { + int count = mChildHelper.getChildCount(); + for (int i = 0; i < count; ++i) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i)); + if (holder.isChanged() && !holder.isRemoved() && !holder.shouldIgnore()) { + long key = getChangedHolderKey(holder); + mState.mOldChangedHolders.put(key, holder); + mState.mPreLayoutHolderMap.remove(holder); + } + } + } + } + mState.mItemCount = mAdapter.getItemCount(); + mState.mDeletedInvisibleItemCountSincePreviousLayout = 0; + + // Step 2: Run layout + mState.mInPreLayout = false; + mLayout.onLayoutChildren(mRecycler, mState); + + mState.mStructureChanged = false; + mPendingSavedState = null; + + // onLayoutChildren may have caused client code to disable item animations; re-check + mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null; + + if (mState.mRunSimpleAnimations) { + // Step 3: Find out where things are now, post-layout + ArrayMap newChangedHolders = mState.mOldChangedHolders != null ? + new ArrayMap() : null; + int count = mChildHelper.getChildCount(); + for (int i = 0; i < count; ++i) { + ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i)); + if (holder.shouldIgnore()) { + continue; + } + final View view = holder.itemView; + long key = getChangedHolderKey(holder); + if (newChangedHolders != null && mState.mOldChangedHolders.get(key) != null) { + newChangedHolders.put(key, holder); + } else { + mState.mPostLayoutHolderMap.put(holder, new ItemHolderInfo(holder, + view.getLeft(), view.getTop(), view.getRight(), view.getBottom())); + } + } + processDisappearingList(appearingViewInitialBounds); + // Step 4: Animate DISAPPEARING and REMOVED items + int preLayoutCount = mState.mPreLayoutHolderMap.size(); + for (int i = preLayoutCount - 1; i >= 0; i--) { + ViewHolder itemHolder = mState.mPreLayoutHolderMap.keyAt(i); + if (!mState.mPostLayoutHolderMap.containsKey(itemHolder)) { + ItemHolderInfo disappearingItem = mState.mPreLayoutHolderMap.valueAt(i); + mState.mPreLayoutHolderMap.removeAt(i); + + View disappearingItemView = disappearingItem.holder.itemView; + mRecycler.unscrapView(disappearingItem.holder); + animateDisappearance(disappearingItem); + } + } + // Step 5: Animate APPEARING and ADDED items + int postLayoutCount = mState.mPostLayoutHolderMap.size(); + if (postLayoutCount > 0) { + for (int i = postLayoutCount - 1; i >= 0; i--) { + ViewHolder itemHolder = mState.mPostLayoutHolderMap.keyAt(i); + ItemHolderInfo info = mState.mPostLayoutHolderMap.valueAt(i); + if ((mState.mPreLayoutHolderMap.isEmpty() || + !mState.mPreLayoutHolderMap.containsKey(itemHolder))) { + mState.mPostLayoutHolderMap.removeAt(i); + Rect initialBounds = (appearingViewInitialBounds != null) ? + appearingViewInitialBounds.get(itemHolder.itemView) : null; + animateAppearance(itemHolder, initialBounds, + info.left, info.top); + } + } + } + // Step 6: Animate PERSISTENT items + count = mState.mPostLayoutHolderMap.size(); + for (int i = 0; i < count; ++i) { + ViewHolder postHolder = mState.mPostLayoutHolderMap.keyAt(i); + ItemHolderInfo postInfo = mState.mPostLayoutHolderMap.valueAt(i); + ItemHolderInfo preInfo = mState.mPreLayoutHolderMap.get(postHolder); + if (preInfo != null && postInfo != null) { + if (preInfo.left != postInfo.left || preInfo.top != postInfo.top) { + postHolder.setIsRecyclable(false); + if (DEBUG) { + Log.d(TAG, "PERSISTENT: " + postHolder + + " with view " + postHolder.itemView); + } + if (mItemAnimator.animateMove(postHolder, + preInfo.left, preInfo.top, postInfo.left, postInfo.top)) { + postAnimationRunner(); + } + } + } + } + // Step 7: Animate CHANGING items + count = mState.mOldChangedHolders != null ? mState.mOldChangedHolders.size() : 0; + // traverse reverse in case view gets recycled while we are traversing the list. + for (int i = count - 1; i >= 0; i--) { + long key = mState.mOldChangedHolders.keyAt(i); + ViewHolder oldHolder = mState.mOldChangedHolders.get(key); + View oldView = oldHolder.itemView; + if (oldHolder.shouldIgnore()) { + continue; + } + // We probably don't need this check anymore since these views are removed from + // the list if they are recycled. + if (mRecycler.mChangedScrap != null && + mRecycler.mChangedScrap.contains(oldHolder)) { + animateChange(oldHolder, newChangedHolders.get(key)); + } else if (DEBUG) { + Log.e(TAG, "cannot find old changed holder in changed scrap :/" + oldHolder); + } + } + } + resumeRequestLayout(false); + mLayout.removeAndRecycleScrapInt(mRecycler); + mState.mPreviousLayoutItemCount = mState.mItemCount; + mDataSetHasChangedAfterLayout = false; + mState.mRunSimpleAnimations = false; + mState.mRunPredictiveAnimations = false; + onExitLayoutOrScroll(); + mLayout.mRequestedSimpleAnimations = false; + if (mRecycler.mChangedScrap != null) { + mRecycler.mChangedScrap.clear(); + } + mState.mOldChangedHolders = null; + + if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) { + dispatchOnScrolled(0, 0); + } + } + + private void findMinMaxChildLayoutPositions(int[] into) { + final int count = mChildHelper.getChildCount(); + if (count == 0) { + into[0] = 0; + into[1] = 0; + return; + } + int minPositionPreLayout = Integer.MAX_VALUE; + int maxPositionPreLayout = Integer.MIN_VALUE; + for (int i = 0; i < count; ++i) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i)); + if (holder.shouldIgnore()) { + continue; + } + final int pos = holder.getLayoutPosition(); + if (pos < minPositionPreLayout) { + minPositionPreLayout = pos; + } + if (pos > maxPositionPreLayout) { + maxPositionPreLayout = pos; + } + } + into[0] = minPositionPreLayout; + into[1] = maxPositionPreLayout; + } + + private boolean didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout) { + int count = mChildHelper.getChildCount(); + if (count == 0) { + return minPositionPreLayout != 0 || maxPositionPreLayout != 0; + } + for (int i = 0; i < count; ++i) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i)); + if (holder.shouldIgnore()) { + continue; + } + final int pos = holder.getLayoutPosition(); + if (pos < minPositionPreLayout || pos > maxPositionPreLayout) { + return true; + } + } + return false; + } + + @Override + protected void removeDetachedView(View child, boolean animate) { + ViewHolder vh = getChildViewHolderInt(child); + if (vh != null) { + if (vh.isTmpDetached()) { + vh.clearTmpDetachFlag(); + } else if (!vh.shouldIgnore()) { + throw new IllegalArgumentException("Called removeDetachedView with a view which" + + " is not flagged as tmp detached." + vh); + } + } + dispatchChildDetached(child); + super.removeDetachedView(child, animate); + } + + /** + * Returns a unique key to be used while handling change animations. + * It might be child's position or stable id depending on the adapter type. + */ + long getChangedHolderKey(ViewHolder holder) { + return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition; + } + + /** + * A LayoutManager may want to layout a view just to animate disappearance. + * This method handles those views and triggers remove animation on them. + */ + private void processDisappearingList(ArrayMap appearingViews) { + final List disappearingList = mState.mDisappearingViewsInLayoutPass; + for (int i = disappearingList.size() - 1; i >= 0; i --) { + View view = disappearingList.get(i); + ViewHolder vh = getChildViewHolderInt(view); + final ItemHolderInfo info = mState.mPreLayoutHolderMap.remove(vh); + if (!mState.isPreLayout()) { + mState.mPostLayoutHolderMap.remove(vh); + } + if (appearingViews.remove(view) != null) { + mLayout.removeAndRecycleView(view, mRecycler); + continue; + } + if (info != null) { + animateDisappearance(info); + } else { + // let it disappear from the position it becomes visible + animateDisappearance(new ItemHolderInfo(vh, view.getLeft(), view.getTop(), + view.getRight(), view.getBottom())); + } + } + disappearingList.clear(); + } + + private void animateAppearance(ViewHolder itemHolder, Rect beforeBounds, int afterLeft, + int afterTop) { + View newItemView = itemHolder.itemView; + if (beforeBounds != null && + (beforeBounds.left != afterLeft || beforeBounds.top != afterTop)) { + // slide items in if before/after locations differ + itemHolder.setIsRecyclable(false); + if (DEBUG) { + Log.d(TAG, "APPEARING: " + itemHolder + " with view " + newItemView); + } + if (mItemAnimator.animateMove(itemHolder, + beforeBounds.left, beforeBounds.top, + afterLeft, afterTop)) { + postAnimationRunner(); + } + } else { + if (DEBUG) { + Log.d(TAG, "ADDED: " + itemHolder + " with view " + newItemView); + } + itemHolder.setIsRecyclable(false); + if (mItemAnimator.animateAdd(itemHolder)) { + postAnimationRunner(); + } + } + } + + private void animateDisappearance(ItemHolderInfo disappearingItem) { + View disappearingItemView = disappearingItem.holder.itemView; + addAnimatingView(disappearingItem.holder); + int oldLeft = disappearingItem.left; + int oldTop = disappearingItem.top; + int newLeft = disappearingItemView.getLeft(); + int newTop = disappearingItemView.getTop(); + if (oldLeft != newLeft || oldTop != newTop) { + disappearingItem.holder.setIsRecyclable(false); + disappearingItemView.layout(newLeft, newTop, + newLeft + disappearingItemView.getWidth(), + newTop + disappearingItemView.getHeight()); + if (DEBUG) { + Log.d(TAG, "DISAPPEARING: " + disappearingItem.holder + + " with view " + disappearingItemView); + } + if (mItemAnimator.animateMove(disappearingItem.holder, oldLeft, oldTop, + newLeft, newTop)) { + postAnimationRunner(); + } + } else { + if (DEBUG) { + Log.d(TAG, "REMOVED: " + disappearingItem.holder + + " with view " + disappearingItemView); + } + disappearingItem.holder.setIsRecyclable(false); + if (mItemAnimator.animateRemove(disappearingItem.holder)) { + postAnimationRunner(); + } + } + } + + private void animateChange(ViewHolder oldHolder, ViewHolder newHolder) { + oldHolder.setIsRecyclable(false); + addAnimatingView(oldHolder); + oldHolder.mShadowedHolder = newHolder; + mRecycler.unscrapView(oldHolder); + if (DEBUG) { + Log.d(TAG, "CHANGED: " + oldHolder + " with view " + oldHolder.itemView); + } + final int fromLeft = oldHolder.itemView.getLeft(); + final int fromTop = oldHolder.itemView.getTop(); + final int toLeft, toTop; + if (newHolder == null || newHolder.shouldIgnore()) { + toLeft = fromLeft; + toTop = fromTop; + } else { + toLeft = newHolder.itemView.getLeft(); + toTop = newHolder.itemView.getTop(); + newHolder.setIsRecyclable(false); + newHolder.mShadowingHolder = oldHolder; + } + if(mItemAnimator.animateChange(oldHolder, newHolder, + fromLeft, fromTop, toLeft, toTop)) { + postAnimationRunner(); + } + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + eatRequestLayout(); + dispatchLayout(); + resumeRequestLayout(false); + mFirstLayoutComplete = true; + } + + @Override + public void requestLayout() { + if (!mEatRequestLayout) { + super.requestLayout(); + } else { + mLayoutRequestEaten = true; + } + } + + void markItemDecorInsetsDirty() { + final int childCount = mChildHelper.getUnfilteredChildCount(); + for (int i = 0; i < childCount; i++) { + final View child = mChildHelper.getUnfilteredChildAt(i); + ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true; + } + mRecycler.markItemDecorInsetsDirty(); + } + + @Override + public void draw(Canvas c) { + super.draw(c); + + final int count = mItemDecorations.size(); + for (int i = 0; i < count; i++) { + mItemDecorations.get(i).onDrawOver(c, this, mState); + } + // TODO If padding is not 0 and chilChildrenToPadding is false, to draw glows properly, we + // need find children closest to edges. Not sure if it is worth the effort. + boolean needsInvalidate = false; + if (mLeftGlow != null && !mLeftGlow.isFinished()) { + final int restore = c.save(); + final int padding = mClipToPadding ? getPaddingBottom() : 0; + c.rotate(270); + c.translate(-getHeight() + padding, 0); + needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c); + c.restoreToCount(restore); + } + if (mTopGlow != null && !mTopGlow.isFinished()) { + final int restore = c.save(); + if (mClipToPadding) { + c.translate(getPaddingLeft(), getPaddingTop()); + } + needsInvalidate |= mTopGlow != null && mTopGlow.draw(c); + c.restoreToCount(restore); + } + if (mRightGlow != null && !mRightGlow.isFinished()) { + final int restore = c.save(); + final int width = getWidth(); + final int padding = mClipToPadding ? getPaddingTop() : 0; + c.rotate(90); + c.translate(-padding, -width); + needsInvalidate |= mRightGlow != null && mRightGlow.draw(c); + c.restoreToCount(restore); + } + if (mBottomGlow != null && !mBottomGlow.isFinished()) { + final int restore = c.save(); + c.rotate(180); + if (mClipToPadding) { + c.translate(-getWidth() + getPaddingRight(), -getHeight() + getPaddingBottom()); + } else { + c.translate(-getWidth(), -getHeight()); + } + needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c); + c.restoreToCount(restore); + } + + // If some views are animating, ItemDecorators are likely to move/change with them. + // Invalidate RecyclerView to re-draw decorators. This is still efficient because children's + // display lists are not invalidated. + if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0 && + mItemAnimator.isRunning()) { + needsInvalidate = true; + } + + if (needsInvalidate) { + ViewCompat.postInvalidateOnAnimation(this); + } + } + + @Override + public void onDraw(Canvas c) { + super.onDraw(c); + + final int count = mItemDecorations.size(); + for (int i = 0; i < count; i++) { + mItemDecorations.get(i).onDraw(c, this, mState); + } + } + + @Override + protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { + return p instanceof LayoutParams && mLayout.checkLayoutParams((LayoutParams) p); + } + + @Override + protected ViewGroup.LayoutParams generateDefaultLayoutParams() { + if (mLayout == null) { + throw new IllegalStateException("RecyclerView has no LayoutManager"); + } + return mLayout.generateDefaultLayoutParams(); + } + + @Override + public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { + if (mLayout == null) { + throw new IllegalStateException("RecyclerView has no LayoutManager"); + } + return mLayout.generateLayoutParams(getContext(), attrs); + } + + @Override + protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { + if (mLayout == null) { + throw new IllegalStateException("RecyclerView has no LayoutManager"); + } + return mLayout.generateLayoutParams(p); + } + + void saveOldPositions() { + final int childCount = mChildHelper.getUnfilteredChildCount(); + for (int i = 0; i < childCount; i++) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i)); + if (DEBUG && holder.mPosition == -1 && !holder.isRemoved()) { + throw new IllegalStateException("view holder cannot have position -1 unless it" + + " is removed"); + } + if (!holder.shouldIgnore()) { + holder.saveOldPosition(); + } + } + } + + void clearOldPositions() { + final int childCount = mChildHelper.getUnfilteredChildCount(); + for (int i = 0; i < childCount; i++) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i)); + if (!holder.shouldIgnore()) { + holder.clearOldPosition(); + } + } + mRecycler.clearOldPositions(); + } + + void offsetPositionRecordsForMove(int from, int to) { + final int childCount = mChildHelper.getUnfilteredChildCount(); + final int start, end, inBetweenOffset; + if (from < to) { + start = from; + end = to; + inBetweenOffset = -1; + } else { + start = to; + end = from; + inBetweenOffset = 1; + } + + for (int i = 0; i < childCount; i++) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i)); + if (holder == null || holder.mPosition < start || holder.mPosition > end) { + continue; + } + if (DEBUG) { + Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder " + + holder); + } + if (holder.mPosition == from) { + holder.offsetPosition(to - from, false); + } else { + holder.offsetPosition(inBetweenOffset, false); + } + + mState.mStructureChanged = true; + } + mRecycler.offsetPositionRecordsForMove(from, to); + requestLayout(); + } + + void offsetPositionRecordsForInsert(int positionStart, int itemCount) { + final int childCount = mChildHelper.getUnfilteredChildCount(); + for (int i = 0; i < childCount; i++) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i)); + if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) { + if (DEBUG) { + Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder " + + holder + " now at position " + (holder.mPosition + itemCount)); + } + holder.offsetPosition(itemCount, false); + mState.mStructureChanged = true; + } + } + mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount); + requestLayout(); + } + + void offsetPositionRecordsForRemove(int positionStart, int itemCount, + boolean applyToPreLayout) { + final int positionEnd = positionStart + itemCount; + final int childCount = mChildHelper.getUnfilteredChildCount(); + for (int i = 0; i < childCount; i++) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i)); + if (holder != null && !holder.shouldIgnore()) { + if (holder.mPosition >= positionEnd) { + if (DEBUG) { + Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i + + " holder " + holder + " now at position " + + (holder.mPosition - itemCount)); + } + holder.offsetPosition(-itemCount, applyToPreLayout); + mState.mStructureChanged = true; + } else if (holder.mPosition >= positionStart) { + if (DEBUG) { + Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i + + " holder " + holder + " now REMOVED"); + } + holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount, + applyToPreLayout); + mState.mStructureChanged = true; + } + } + } + mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout); + requestLayout(); + } + + /** + * Rebind existing views for the given range, or create as needed. + * + * @param positionStart Adapter position to start at + * @param itemCount Number of views that must explicitly be rebound + */ + void viewRangeUpdate(int positionStart, int itemCount) { + final int childCount = mChildHelper.getUnfilteredChildCount(); + final int positionEnd = positionStart + itemCount; + + for (int i = 0; i < childCount; i++) { + final View child = mChildHelper.getUnfilteredChildAt(i); + final ViewHolder holder = getChildViewHolderInt(child); + if (holder == null || holder.shouldIgnore()) { + continue; + } + if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) { + // We re-bind these view holders after pre-processing is complete so that + // ViewHolders have their final positions assigned. + holder.addFlags(ViewHolder.FLAG_UPDATE); + if (supportsChangeAnimations()) { + holder.addFlags(ViewHolder.FLAG_CHANGED); + } + // lp cannot be null since we get ViewHolder from it. + ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true; + } + } + mRecycler.viewRangeUpdate(positionStart, itemCount); + } + + void rebindUpdatedViewHolders() { + final int childCount = mChildHelper.getChildCount(); + for (int i = 0; i < childCount; i++) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i)); + // validate type is correct + if (holder == null || holder.shouldIgnore()) { + continue; + } + if (holder.isRemoved() || holder.isInvalid()) { + requestLayout(); + } else if (holder.needsUpdate()) { + final int type = mAdapter.getItemViewType(holder.mPosition); + if (holder.getItemViewType() == type) { + // Binding an attached view will request a layout if needed. + if (!holder.isChanged() || !supportsChangeAnimations()) { + mAdapter.bindViewHolder(holder, holder.mPosition); + } else { + // Don't rebind changed holders if change animations are enabled. + // We want the old contents for the animation and will get a new + // holder for the new contents. + requestLayout(); + } + } else { + // binding to a new view will need re-layout anyways. We can as well trigger + // it here so that it happens during layout + requestLayout(); + break; + } + } + } + } + + private void setDataSetChangedAfterLayout() { + if (mDataSetHasChangedAfterLayout) { + return; + } + mDataSetHasChangedAfterLayout = true; + final int childCount = mChildHelper.getUnfilteredChildCount(); + for (int i = 0; i < childCount; i++) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i)); + if (holder != null && !holder.shouldIgnore()) { + holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN); + } + } + mRecycler.setAdapterPositionsAsUnknown(); + } + + /** + * Mark all known views as invalid. Used in response to a, "the whole world might have changed" + * data change event. + */ + void markKnownViewsInvalid() { + final int childCount = mChildHelper.getUnfilteredChildCount(); + for (int i = 0; i < childCount; i++) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i)); + if (holder != null && !holder.shouldIgnore()) { + holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID); + } + } + markItemDecorInsetsDirty(); + mRecycler.markKnownViewsInvalid(); + } + + /** + * Invalidates all ItemDecorations. If RecyclerView has item decorations, calling this method + * will trigger a {@link #requestLayout()} call. + */ + public void invalidateItemDecorations() { + if (mItemDecorations.size() == 0) { + return; + } + if (mLayout != null) { + mLayout.assertNotInLayoutOrScroll("Cannot invalidate item decorations during a scroll" + + " or layout"); + } + markItemDecorInsetsDirty(); + requestLayout(); + } + + /** + * Retrieve the {@link ViewHolder} for the given child view. + * + * @param child Child of this RecyclerView to query for its ViewHolder + * @return The child view's ViewHolder + */ + public ViewHolder getChildViewHolder(View child) { + final ViewParent parent = child.getParent(); + if (parent != null && parent != this) { + throw new IllegalArgumentException("View " + child + " is not a direct child of " + + this); + } + return getChildViewHolderInt(child); + } + + static ViewHolder getChildViewHolderInt(View child) { + if (child == null) { + return null; + } + return ((LayoutParams) child.getLayoutParams()).mViewHolder; + } + + /** + * @deprecated use {@link #getChildAdapterPosition(View)} or + * {@link #getChildLayoutPosition(View)}. + */ + @Deprecated + public int getChildPosition(View child) { + return getChildAdapterPosition(child); + } + + /** + * Return the adapter position that the given child view corresponds to. + * + * @param child Child View to query + * @return Adapter position corresponding to the given view or {@link #NO_POSITION} + */ + public int getChildAdapterPosition(View child) { + final ViewHolder holder = getChildViewHolderInt(child); + return holder != null ? holder.getAdapterPosition() : NO_POSITION; + } + + /** + * Return the adapter position of the given child view as of the latest completed layout pass. + *

+ * This position may not be equal to Item's adapter position if there are pending changes + * in the adapter which have not been reflected to the layout yet. + * + * @param child Child View to query + * @return Adapter position of the given View as of last layout pass or {@link #NO_POSITION} if + * the View is representing a removed item. + */ + public int getChildLayoutPosition(View child) { + final ViewHolder holder = getChildViewHolderInt(child); + return holder != null ? holder.getLayoutPosition() : NO_POSITION; + } + + /** + * Return the stable item id that the given child view corresponds to. + * + * @param child Child View to query + * @return Item id corresponding to the given view or {@link #NO_ID} + */ + public long getChildItemId(View child) { + if (mAdapter == null || !mAdapter.hasStableIds()) { + return NO_ID; + } + final ViewHolder holder = getChildViewHolderInt(child); + return holder != null ? holder.getItemId() : NO_ID; + } + + /** + * @deprecated use {@link #findViewHolderForLayoutPosition(int)} or + * {@link #findViewHolderForAdapterPosition(int)} + */ + @Deprecated + public ViewHolder findViewHolderForPosition(int position) { + return findViewHolderForPosition(position, false); + } + + /** + * Return the ViewHolder for the item in the given position of the data set as of the latest + * layout pass. + *

+ * This method checks only the children of RecyclerView. If the item at the given + * position is not laid out, it will not create a new one. + *

+ * Note that when Adapter contents change, ViewHolder positions are not updated until the + * next layout calculation. If there are pending adapter updates, the return value of this + * method may not match your adapter contents. You can use + * #{@link ViewHolder#getAdapterPosition()} to get the current adapter position of a ViewHolder. + * + * @param position The position of the item in the data set of the adapter + * @return The ViewHolder at position or null if there is no such item + */ + public ViewHolder findViewHolderForLayoutPosition(int position) { + return findViewHolderForPosition(position, false); + } + + /** + * Return the ViewHolder for the item in the given position of the data set. Unlike + * {@link #findViewHolderForLayoutPosition(int)} this method takes into account any pending + * adapter changes that may not be reflected to the layout yet. On the other hand, if + * {@link Adapter#notifyDataSetChanged()} has been called but the new layout has not been + * calculated yet, this method will return null since the new positions of views + * are unknown until the layout is calculated. + *

+ * This method checks only the children of RecyclerView. If the item at the given + * position is not laid out, it will not create a new one. + * + * @param position The position of the item in the data set of the adapter + * @return The ViewHolder at position or null if there is no such item + */ + public ViewHolder findViewHolderForAdapterPosition(int position) { + if (mDataSetHasChangedAfterLayout) { + return null; + } + final int childCount = mChildHelper.getUnfilteredChildCount(); + for (int i = 0; i < childCount; i++) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i)); + if (holder != null && !holder.isRemoved() && getAdapterPositionFor(holder) == position) { + return holder; + } + } + return null; + } + + ViewHolder findViewHolderForPosition(int position, boolean checkNewPosition) { + final int childCount = mChildHelper.getUnfilteredChildCount(); + for (int i = 0; i < childCount; i++) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i)); + if (holder != null && !holder.isRemoved()) { + if (checkNewPosition) { + if (holder.mPosition == position) { + return holder; + } + } else if (holder.getLayoutPosition() == position) { + return holder; + } + } + } + // This method should not query cached views. It creates a problem during adapter updates + // when we are dealing with already laid out views. Also, for the public method, it is more + // reasonable to return null if position is not laid out. + return null; + } + + /** + * Return the ViewHolder for the item with the given id. The RecyclerView must + * use an Adapter with {@link Adapter#setHasStableIds(boolean) stableIds} to + * return a non-null value. + *

+ * This method checks only the children of RecyclerView. If the item with the given + * id is not laid out, it will not create a new one. + * + * @param id The id for the requested item + * @return The ViewHolder with the given id or null if there is no such item + */ + public ViewHolder findViewHolderForItemId(long id) { + final int childCount = mChildHelper.getUnfilteredChildCount(); + for (int i = 0; i < childCount; i++) { + final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i)); + if (holder != null && holder.getItemId() == id) { + return holder; + } + } + // this method should not query cached views. They are not children so they + // should not be returned in this public method + return null; + } + + /** + * Find the topmost view under the given point. + * + * @param x Horizontal position in pixels to search + * @param y Vertical position in pixels to search + * @return The child view under (x, y) or null if no matching child is found + */ + public View findChildViewUnder(float x, float y) { + final int count = mChildHelper.getChildCount(); + for (int i = count - 1; i >= 0; i--) { + final View child = mChildHelper.getChildAt(i); + final float translationX = ViewCompat.getTranslationX(child); + final float translationY = ViewCompat.getTranslationY(child); + if (x >= child.getLeft() + translationX && + x <= child.getRight() + translationX && + y >= child.getTop() + translationY && + y <= child.getBottom() + translationY) { + return child; + } + } + return null; + } + + /** + * Offset the bounds of all child views by dy pixels. + * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}. + * + * @param dy Vertical pixel offset to apply to the bounds of all child views + */ + public void offsetChildrenVertical(int dy) { + final int childCount = mChildHelper.getChildCount(); + for (int i = 0; i < childCount; i++) { + mChildHelper.getChildAt(i).offsetTopAndBottom(dy); + } + } + + /** + * Called when an item view is attached to this RecyclerView. + * + *

Subclasses of RecyclerView may want to perform extra bookkeeping or modifications + * of child views as they become attached. This will be called before a + * {@link LayoutManager} measures or lays out the view and is a good time to perform these + * changes.

+ * + * @param child Child view that is now attached to this RecyclerView and its associated window + */ + public void onChildAttachedToWindow(View child) { + } + + /** + * Called when an item view is detached from this RecyclerView. + * + *

Subclasses of RecyclerView may want to perform extra bookkeeping or modifications + * of child views as they become detached. This will be called as a + * {@link LayoutManager} fully detaches the child view from the parent and its window.

+ * + * @param child Child view that is now detached from this RecyclerView and its associated window + */ + public void onChildDetachedFromWindow(View child) { + } + + /** + * Offset the bounds of all child views by dx pixels. + * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}. + * + * @param dx Horizontal pixel offset to apply to the bounds of all child views + */ + public void offsetChildrenHorizontal(int dx) { + final int childCount = mChildHelper.getChildCount(); + for (int i = 0; i < childCount; i++) { + mChildHelper.getChildAt(i).offsetLeftAndRight(dx); + } + } + + Rect getItemDecorInsetsForChild(View child) { + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (!lp.mInsetsDirty) { + return lp.mDecorInsets; + } + + final Rect insets = lp.mDecorInsets; + insets.set(0, 0, 0, 0); + final int decorCount = mItemDecorations.size(); + for (int i = 0; i < decorCount; i++) { + mTempRect.set(0, 0, 0, 0); + mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState); + insets.left += mTempRect.left; + insets.top += mTempRect.top; + insets.right += mTempRect.right; + insets.bottom += mTempRect.bottom; + } + lp.mInsetsDirty = false; + return insets; + } + + /** + * Called when the scroll position of this RecyclerView changes. Subclasses should use + * this method to respond to scrolling within the adapter's data set instead of an explicit + * listener. + * + *

This method will always be invoked before listeners. If a subclass needs to perform + * any additional upkeep or bookkeeping after scrolling but before listeners run, + * this is a good place to do so.

+ * + *

This differs from {@link View#onScrollChanged(int, int, int, int)} in that it receives + * the distance scrolled in either direction within the adapter's data set instead of absolute + * scroll coordinates. Since RecyclerView cannot compute the absolute scroll position from + * any arbitrary point in the data set, onScrollChanged will always receive + * the current {@link View#getScrollX()} and {@link View#getScrollY()} values which + * do not correspond to the data set scroll position. However, some subclasses may choose + * to use these fields as special offsets.

+ * + * @param dx horizontal distance scrolled in pixels + * @param dy vertical distance scrolled in pixels + */ + public void onScrolled(int dx, int dy) { + // Do nothing + } + + void dispatchOnScrolled(int hresult, int vresult) { + // Pass the current scrollX/scrollY values; no actual change in these properties occurred + // but some general-purpose code may choose to respond to changes this way. + final int scrollX = getScrollX(); + final int scrollY = getScrollY(); + onScrollChanged(scrollX, scrollY, scrollX, scrollY); + + // Pass the real deltas to onScrolled, the RecyclerView-specific method. + onScrolled(hresult, vresult); + + // Invoke listeners last. Subclassed view methods always handle the event first. + // All internal state is consistent by the time listeners are invoked. + if (mScrollListener != null) { + mScrollListener.onScrolled(this, hresult, vresult); + } + if (mScrollListeners != null) { + for (int i = mScrollListeners.size() - 1; i >= 0; i--) { + mScrollListeners.get(i).onScrolled(this, hresult, vresult); + } + } + } + + /** + * Called when the scroll state of this RecyclerView changes. Subclasses should use this + * method to respond to state changes instead of an explicit listener. + * + *

This method will always be invoked before listeners, but after the LayoutManager + * responds to the scroll state change.

+ * + * @param state the new scroll state, one of {@link #SCROLL_STATE_IDLE}, + * {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING} + */ + public void onScrollStateChanged(int state) { + // Do nothing + } + + void dispatchOnScrollStateChanged(int state) { + // Let the LayoutManager go first; this allows it to bring any properties into + // a consistent state before the RecyclerView subclass responds. + if (mLayout != null) { + mLayout.onScrollStateChanged(state); + } + + // Let the RecyclerView subclass handle this event next; any LayoutManager property + // changes will be reflected by this time. + onScrollStateChanged(state); + + // Listeners go last. All other internal state is consistent by this point. + if (mScrollListener != null) { + mScrollListener.onScrollStateChanged(this, state); + } + if (mScrollListeners != null) { + for (int i = mScrollListeners.size() - 1; i >= 0; i--) { + mScrollListeners.get(i).onScrollStateChanged(this, state); + } + } + } + + /** + * Returns whether there are pending adapter updates which are not yet applied to the layout. + *

+ * If this method returns true, it means that what user is currently seeing may not + * reflect them adapter contents (depending on what has changed). + * You may use this information to defer or cancel some operations. + *

+ * This method returns true if RecyclerView has not yet calculated the first layout after it is + * attached to the Window or the Adapter has been replaced. + * + * @return True if there are some adapter updates which are not yet reflected to layout or false + * if layout is up to date. + */ + public boolean hasPendingAdapterUpdates() { + return !mFirstLayoutComplete || mDataSetHasChangedAfterLayout + || mAdapterHelper.hasPendingUpdates(); + } + + private class ViewFlinger implements Runnable { + private int mLastFlingX; + private int mLastFlingY; + private ScrollerCompat mScroller; + private Interpolator mInterpolator = sQuinticInterpolator; + + + // When set to true, postOnAnimation callbacks are delayed until the run method completes + private boolean mEatRunOnAnimationRequest = false; + + // Tracks if postAnimationCallback should be re-attached when it is done + private boolean mReSchedulePostAnimationCallback = false; + + public ViewFlinger() { + mScroller = ScrollerCompat.create(getContext(), sQuinticInterpolator); + } + + @Override + public void run() { + disableRunOnAnimationRequests(); + consumePendingUpdateOperations(); + // keep a local reference so that if it is changed during onAnimation method, it won't + // cause unexpected behaviors + final ScrollerCompat scroller = mScroller; + final SmoothScroller smoothScroller = mLayout.mSmoothScroller; + if (scroller.computeScrollOffset()) { + final int x = scroller.getCurrX(); + final int y = scroller.getCurrY(); + final int dx = x - mLastFlingX; + final int dy = y - mLastFlingY; + int hresult = 0; + int vresult = 0; + mLastFlingX = x; + mLastFlingY = y; + int overscrollX = 0, overscrollY = 0; + if (mAdapter != null) { + eatRequestLayout(); + onEnterLayoutOrScroll(); + if (dx != 0) { + hresult = mLayout.scrollHorizontallyBy(dx, mRecycler, mState); + overscrollX = dx - hresult; + } + if (dy != 0) { + vresult = mLayout.scrollVerticallyBy(dy, mRecycler, mState); + overscrollY = dy - vresult; + } + if (supportsChangeAnimations()) { + // Fix up shadow views used by changing animations + int count = mChildHelper.getChildCount(); + for (int i = 0; i < count; i++) { + View view = mChildHelper.getChildAt(i); + ViewHolder holder = getChildViewHolder(view); + if (holder != null && holder.mShadowingHolder != null) { + View shadowingView = holder.mShadowingHolder.itemView; + int left = view.getLeft(); + int top = view.getTop(); + if (left != shadowingView.getLeft() || + top != shadowingView.getTop()) { + shadowingView.layout(left, top, + left + shadowingView.getWidth(), + top + shadowingView.getHeight()); + } + } + } + } + + if (smoothScroller != null && !smoothScroller.isPendingInitialRun() && + smoothScroller.isRunning()) { + final int adapterSize = mState.getItemCount(); + if (adapterSize == 0) { + smoothScroller.stop(); + } else if (smoothScroller.getTargetPosition() >= adapterSize) { + smoothScroller.setTargetPosition(adapterSize - 1); + smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY); + } else { + smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY); + } + } + onExitLayoutOrScroll(); + resumeRequestLayout(false); + } + if (!mItemDecorations.isEmpty()) { + invalidate(); + } + if (ViewCompat.getOverScrollMode(RecyclerView.this) != + ViewCompat.OVER_SCROLL_NEVER) { + considerReleasingGlowsOnScroll(dx, dy); + } + if (overscrollX != 0 || overscrollY != 0) { + final int vel = (int) scroller.getCurrVelocity(); + + int velX = 0; + if (overscrollX != x) { + velX = overscrollX < 0 ? -vel : overscrollX > 0 ? vel : 0; + } + + int velY = 0; + if (overscrollY != y) { + velY = overscrollY < 0 ? -vel : overscrollY > 0 ? vel : 0; + } + + if (ViewCompat.getOverScrollMode(RecyclerView.this) != + ViewCompat.OVER_SCROLL_NEVER) { + absorbGlows(velX, velY); + } + if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0) && + (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) { + scroller.abortAnimation(); + } + } + if (hresult != 0 || vresult != 0) { + dispatchOnScrolled(hresult, vresult); + } + + if (!awakenScrollBars()) { + invalidate(); + } + + final boolean fullyConsumedVertical = dy != 0 && mLayout.canScrollVertically() + && vresult == dy; + final boolean fullyConsumedHorizontal = dx != 0 && mLayout.canScrollHorizontally() + && hresult == dx; + final boolean fullyConsumedAny = (dx == 0 && dy == 0) || fullyConsumedHorizontal + || fullyConsumedVertical; + + if (scroller.isFinished() || !fullyConsumedAny) { + setScrollState(SCROLL_STATE_IDLE); // setting state to idle will stop this. + } else { + postOnAnimation(); + } + } + // call this after the onAnimation is complete not to have inconsistent callbacks etc. + if (smoothScroller != null && smoothScroller.isPendingInitialRun()) { + smoothScroller.onAnimation(0, 0); + } + enableRunOnAnimationRequests(); + } + + private void disableRunOnAnimationRequests() { + mReSchedulePostAnimationCallback = false; + mEatRunOnAnimationRequest = true; + } + + private void enableRunOnAnimationRequests() { + mEatRunOnAnimationRequest = false; + if (mReSchedulePostAnimationCallback) { + postOnAnimation(); + } + } + + void postOnAnimation() { + if (mEatRunOnAnimationRequest) { + mReSchedulePostAnimationCallback = true; + } else { + removeCallbacks(this); + ViewCompat.postOnAnimation(RecyclerView.this, this); + } + } + + public void fling(int velocityX, int velocityY) { + setScrollState(SCROLL_STATE_SETTLING); + mLastFlingX = mLastFlingY = 0; + mScroller.fling(0, 0, velocityX, velocityY, + Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE); + postOnAnimation(); + } + + public void smoothScrollBy(int dx, int dy) { + smoothScrollBy(dx, dy, 0, 0); + } + + public void smoothScrollBy(int dx, int dy, int vx, int vy) { + smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, vx, vy)); + } + + private float distanceInfluenceForSnapDuration(float f) { + f -= 0.5f; // center the values about 0. + f *= 0.3f * Math.PI / 2.0f; + return (float) Math.sin(f); + } + + private int computeScrollDuration(int dx, int dy, int vx, int vy) { + final int absDx = Math.abs(dx); + final int absDy = Math.abs(dy); + final boolean horizontal = absDx > absDy; + final int velocity = (int) Math.sqrt(vx * vx + vy * vy); + final int delta = (int) Math.sqrt(dx * dx + dy * dy); + final int containerSize = horizontal ? getWidth() : getHeight(); + final int halfContainerSize = containerSize / 2; + final float distanceRatio = Math.min(1.f, 1.f * delta / containerSize); + final float distance = halfContainerSize + halfContainerSize * + distanceInfluenceForSnapDuration(distanceRatio); + + final int duration; + if (velocity > 0) { + duration = 4 * Math.round(1000 * Math.abs(distance / velocity)); + } else { + float absDelta = (float) (horizontal ? absDx : absDy); + duration = (int) (((absDelta / containerSize) + 1) * 300); + } + return Math.min(duration, MAX_SCROLL_DURATION); + } + + public void smoothScrollBy(int dx, int dy, int duration) { + smoothScrollBy(dx, dy, duration, sQuinticInterpolator); + } + + public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) { + if (mInterpolator != interpolator) { + mInterpolator = interpolator; + mScroller = ScrollerCompat.create(getContext(), interpolator); + } + setScrollState(SCROLL_STATE_SETTLING); + mLastFlingX = mLastFlingY = 0; + mScroller.startScroll(0, 0, dx, dy, duration); + postOnAnimation(); + } + + public void stop() { + removeCallbacks(this); + mScroller.abortAnimation(); + } + + } + + private class RecyclerViewDataObserver extends AdapterDataObserver { + @Override + public void onChanged() { + assertNotInLayoutOrScroll(null); + if (mAdapter.hasStableIds()) { + // TODO Determine what actually changed. + // This is more important to implement now since this callback will disable all + // animations because we cannot rely on positions. + mState.mStructureChanged = true; + setDataSetChangedAfterLayout(); + } else { + mState.mStructureChanged = true; + setDataSetChangedAfterLayout(); + } + if (!mAdapterHelper.hasPendingUpdates()) { + requestLayout(); + } + } + + @Override + public void onItemRangeChanged(int positionStart, int itemCount) { + assertNotInLayoutOrScroll(null); + if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount)) { + triggerUpdateProcessor(); + } + } + + @Override + public void onItemRangeInserted(int positionStart, int itemCount) { + assertNotInLayoutOrScroll(null); + if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) { + triggerUpdateProcessor(); + } + } + + @Override + public void onItemRangeRemoved(int positionStart, int itemCount) { + assertNotInLayoutOrScroll(null); + if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) { + triggerUpdateProcessor(); + } + } + + @Override + public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { + assertNotInLayoutOrScroll(null); + if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) { + triggerUpdateProcessor(); + } + } + + void triggerUpdateProcessor() { + if (mPostUpdatesOnAnimation && mHasFixedSize && mIsAttached) { + ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable); + } else { + mAdapterUpdateDuringMeasure = true; + requestLayout(); + } + } + } + + /** + * RecycledViewPool lets you share Views between multiple RecyclerViews. + *

+ * If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool + * and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}. + *

+ * RecyclerView automatically creates a pool for itself if you don't provide one. + * + */ + public static class RecycledViewPool { + private SparseArray> mScrap = + new SparseArray>(); + private SparseIntArray mMaxScrap = new SparseIntArray(); + private int mAttachCount = 0; + + private static final int DEFAULT_MAX_SCRAP = 5; + + public void clear() { + mScrap.clear(); + } + + public void setMaxRecycledViews(int viewType, int max) { + mMaxScrap.put(viewType, max); + final ArrayList scrapHeap = mScrap.get(viewType); + if (scrapHeap != null) { + while (scrapHeap.size() > max) { + scrapHeap.remove(scrapHeap.size() - 1); + } + } + } + + public ViewHolder getRecycledView(int viewType) { + final ArrayList scrapHeap = mScrap.get(viewType); + if (scrapHeap != null && !scrapHeap.isEmpty()) { + final int index = scrapHeap.size() - 1; + final ViewHolder scrap = scrapHeap.get(index); + scrapHeap.remove(index); + return scrap; + } + return null; + } + + int size() { + int count = 0; + for (int i = 0; i < mScrap.size(); i ++) { + ArrayList viewHolders = mScrap.valueAt(i); + if (viewHolders != null) { + count += viewHolders.size(); + } + } + return count; + } + + public void putRecycledView(ViewHolder scrap) { + final int viewType = scrap.getItemViewType(); + final ArrayList scrapHeap = getScrapHeapForType(viewType); + if (mMaxScrap.get(viewType) <= scrapHeap.size()) { + return; + } + scrap.resetInternal(); + scrapHeap.add(scrap); + } + + void attach(Adapter adapter) { + mAttachCount++; + } + + void detach() { + mAttachCount--; + } + + + /** + * Detaches the old adapter and attaches the new one. + *

+ * RecycledViewPool will clear its cache if it has only one adapter attached and the new + * adapter uses a different ViewHolder than the oldAdapter. + * + * @param oldAdapter The previous adapter instance. Will be detached. + * @param newAdapter The new adapter instance. Will be attached. + * @param compatibleWithPrevious True if both oldAdapter and newAdapter are using the same + * ViewHolder and view types. + */ + void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter, + boolean compatibleWithPrevious) { + if (oldAdapter != null) { + detach(); + } + if (!compatibleWithPrevious && mAttachCount == 0) { + clear(); + } + if (newAdapter != null) { + attach(newAdapter); + } + } + + private ArrayList getScrapHeapForType(int viewType) { + ArrayList scrap = mScrap.get(viewType); + if (scrap == null) { + scrap = new ArrayList(); + mScrap.put(viewType, scrap); + if (mMaxScrap.indexOfKey(viewType) < 0) { + mMaxScrap.put(viewType, DEFAULT_MAX_SCRAP); + } + } + return scrap; + } + } + + /** + * A Recycler is responsible for managing scrapped or detached item views for reuse. + * + *

A "scrapped" view is a view that is still attached to its parent RecyclerView but + * that has been marked for removal or reuse.

+ * + *

Typical use of a Recycler by a {@link LayoutManager} will be to obtain views for + * an adapter's data set representing the data at a given position or item ID. + * If the view to be reused is considered "dirty" the adapter will be asked to rebind it. + * If not, the view can be quickly reused by the LayoutManager with no further work. + * Clean views that have not {@link android.view.View#isLayoutRequested() requested layout} + * may be repositioned by a LayoutManager without remeasurement.

+ */ + public final class Recycler { + final ArrayList mAttachedScrap = new ArrayList(); + private ArrayList mChangedScrap = null; + + final ArrayList mCachedViews = new ArrayList(); + + private final List + mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap); + + private int mViewCacheMax = DEFAULT_CACHE_SIZE; + + private RecycledViewPool mRecyclerPool; + + private ViewCacheExtension mViewCacheExtension; + + private static final int DEFAULT_CACHE_SIZE = 2; + + /** + * Clear scrap views out of this recycler. Detached views contained within a + * recycled view pool will remain. + */ + public void clear() { + mAttachedScrap.clear(); + recycleAndClearCachedViews(); + } + + /** + * Set the maximum number of detached, valid views we should retain for later use. + * + * @param viewCount Number of views to keep before sending views to the shared pool + */ + public void setViewCacheSize(int viewCount) { + mViewCacheMax = viewCount; + // first, try the views that can be recycled + for (int i = mCachedViews.size() - 1; i >= 0 && mCachedViews.size() > viewCount; i--) { + recycleCachedViewAt(i); + } + } + + /** + * Returns an unmodifiable list of ViewHolders that are currently in the scrap list. + * + * @return List of ViewHolders in the scrap list. + */ + public List getScrapList() { + return mUnmodifiableAttachedScrap; + } + + /** + * Helper method for getViewForPosition. + *

+ * Checks whether a given view holder can be used for the provided position. + * + * @param holder ViewHolder + * @return true if ViewHolder matches the provided position, false otherwise + */ + boolean validateViewHolderForOffsetPosition(ViewHolder holder) { + // if it is a removed holder, nothing to verify since we cannot ask adapter anymore + // if it is not removed, verify the type and id. + if (holder.isRemoved()) { + return true; + } + if (holder.mPosition < 0 || holder.mPosition >= mAdapter.getItemCount()) { + throw new IndexOutOfBoundsException("Inconsistency detected. Invalid view holder " + + "adapter position" + holder); + } + if (!mState.isPreLayout()) { + // don't check type if it is pre-layout. + final int type = mAdapter.getItemViewType(holder.mPosition); + if (type != holder.getItemViewType()) { + return false; + } + } + if (mAdapter.hasStableIds()) { + return holder.getItemId() == mAdapter.getItemId(holder.mPosition); + } + return true; + } + + /** + * Binds the given View to the position. The View can be a View previously retrieved via + * {@link #getViewForPosition(int)} or created by + * {@link Adapter#onCreateViewHolder(ViewGroup, int)}. + *

+ * Generally, a LayoutManager should acquire its views via {@link #getViewForPosition(int)} + * and let the RecyclerView handle caching. This is a helper method for LayoutManager who + * wants to handle its own recycling logic. + *

+ * Note that, {@link #getViewForPosition(int)} already binds the View to the position so + * you don't need to call this method unless you want to bind this View to another position. + * + * @param view The view to update. + * @param position The position of the item to bind to this View. + */ + public void bindViewToPosition(View view, int position) { + ViewHolder holder = getChildViewHolderInt(view); + if (holder == null) { + throw new IllegalArgumentException("The view does not have a ViewHolder. You cannot" + + " pass arbitrary views to this method, they should be created by the " + + "Adapter"); + } + final int offsetPosition = mAdapterHelper.findPositionOffset(position); + if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) { + throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item " + + "position " + position + "(offset:" + offsetPosition + ")." + + "state:" + mState.getItemCount()); + } + holder.mOwnerRecyclerView = RecyclerView.this; + mAdapter.bindViewHolder(holder, offsetPosition); + attachAccessibilityDelegate(view); + if (mState.isPreLayout()) { + holder.mPreLayoutPosition = position; + } + + final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams(); + final LayoutParams rvLayoutParams; + if (lp == null) { + rvLayoutParams = (LayoutParams) generateDefaultLayoutParams(); + holder.itemView.setLayoutParams(rvLayoutParams); + } else if (!checkLayoutParams(lp)) { + rvLayoutParams = (LayoutParams) generateLayoutParams(lp); + holder.itemView.setLayoutParams(rvLayoutParams); + } else { + rvLayoutParams = (LayoutParams) lp; + } + + rvLayoutParams.mInsetsDirty = true; + rvLayoutParams.mViewHolder = holder; + rvLayoutParams.mPendingInvalidate = holder.itemView.getParent() == null; + } + + /** + * RecyclerView provides artificial position range (item count) in pre-layout state and + * automatically maps these positions to {@link Adapter} positions when + * {@link #getViewForPosition(int)} or {@link #bindViewToPosition(View, int)} is called. + *

+ * Usually, LayoutManager does not need to worry about this. However, in some cases, your + * LayoutManager may need to call some custom component with item positions in which + * case you need the actual adapter position instead of the pre layout position. You + * can use this method to convert a pre-layout position to adapter (post layout) position. + *

+ * Note that if the provided position belongs to a deleted ViewHolder, this method will + * return -1. + *

+ * Calling this method in post-layout state returns the same value back. + * + * @param position The pre-layout position to convert. Must be greater or equal to 0 and + * less than {@link State#getItemCount()}. + */ + public int convertPreLayoutPositionToPostLayout(int position) { + if (position < 0 || position >= mState.getItemCount()) { + throw new IndexOutOfBoundsException("invalid position " + position + ". State " + + "item count is " + mState.getItemCount()); + } + if (!mState.isPreLayout()) { + return position; + } + return mAdapterHelper.findPositionOffset(position); + } + + /** + * Obtain a view initialized for the given position. + * + * This method should be used by {@link LayoutManager} implementations to obtain + * views to represent data from an {@link Adapter}. + *

+ * The Recycler may reuse a scrap or detached view from a shared pool if one is + * available for the correct view type. If the adapter has not indicated that the + * data at the given position has changed, the Recycler will attempt to hand back + * a scrap view that was previously initialized for that data without rebinding. + * + * @param position Position to obtain a view for + * @return A view representing the data at position from adapter + */ + public View getViewForPosition(int position) { + return getViewForPosition(position, false); + } + + View getViewForPosition(int position, boolean dryRun) { + if (position < 0 || position >= mState.getItemCount()) { + throw new IndexOutOfBoundsException("Invalid item position " + position + + "(" + position + "). Item count:" + mState.getItemCount()); + } + boolean fromScrap = false; + ViewHolder holder = null; + // 0) If there is a changed scrap, try to find from there + if (mState.isPreLayout()) { + holder = getChangedScrapViewForPosition(position); + fromScrap = holder != null; + } + // 1) Find from scrap by position + if (holder == null) { + holder = getScrapViewForPosition(position, INVALID_TYPE, dryRun); + if (holder != null) { + if (!validateViewHolderForOffsetPosition(holder)) { + // recycle this scrap + if (!dryRun) { + // we would like to recycle this but need to make sure it is not used by + // animation logic etc. + holder.addFlags(ViewHolder.FLAG_INVALID); + if (holder.isScrap()) { + removeDetachedView(holder.itemView, false); + holder.unScrap(); + } else if (holder.wasReturnedFromScrap()) { + holder.clearReturnedFromScrapFlag(); + } + recycleViewHolderInternal(holder); + } + holder = null; + } else { + fromScrap = true; + } + } + } + if (holder == null) { + final int offsetPosition = mAdapterHelper.findPositionOffset(position); + if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) { + throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item " + + "position " + position + "(offset:" + offsetPosition + ")." + + "state:" + mState.getItemCount()); + } + + final int type = mAdapter.getItemViewType(offsetPosition); + // 2) Find from scrap via stable ids, if exists + if (mAdapter.hasStableIds()) { + holder = getScrapViewForId(mAdapter.getItemId(offsetPosition), type, dryRun); + if (holder != null) { + // update position + holder.mPosition = offsetPosition; + fromScrap = true; + } + } + if (holder == null && mViewCacheExtension != null) { + // We are NOT sending the offsetPosition because LayoutManager does not + // know it. + final View view = mViewCacheExtension + .getViewForPositionAndType(this, position, type); + if (view != null) { + holder = getChildViewHolder(view); + if (holder == null) { + throw new IllegalArgumentException("getViewForPositionAndType returned" + + " a view which does not have a ViewHolder"); + } else if (holder.shouldIgnore()) { + throw new IllegalArgumentException("getViewForPositionAndType returned" + + " a view that is ignored. You must call stopIgnoring before" + + " returning this view."); + } + } + } + if (holder == null) { // fallback to recycler + // try recycler. + // Head to the shared pool. + if (DEBUG) { + Log.d(TAG, "getViewForPosition(" + position + ") fetching from shared " + + "pool"); + } + holder = getRecycledViewPool().getRecycledView(type); + if (holder != null) { + holder.resetInternal(); + if (FORCE_INVALIDATE_DISPLAY_LIST) { + invalidateDisplayListInt(holder); + } + } + } + if (holder == null) { + holder = mAdapter.createViewHolder(RecyclerView.this, type); + if (DEBUG) { + Log.d(TAG, "getViewForPosition created new ViewHolder"); + } + } + } + boolean bound = false; + if (mState.isPreLayout() && holder.isBound()) { + // do not update unless we absolutely have to. + holder.mPreLayoutPosition = position; + } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) { + if (DEBUG && holder.isRemoved()) { + throw new IllegalStateException("Removed holder should be bound and it should" + + " come here only in pre-layout. Holder: " + holder); + } + final int offsetPosition = mAdapterHelper.findPositionOffset(position); + holder.mOwnerRecyclerView = RecyclerView.this; + mAdapter.bindViewHolder(holder, offsetPosition); + attachAccessibilityDelegate(holder.itemView); + bound = true; + if (mState.isPreLayout()) { + holder.mPreLayoutPosition = position; + } + } + + final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams(); + final LayoutParams rvLayoutParams; + if (lp == null) { + rvLayoutParams = (LayoutParams) generateDefaultLayoutParams(); + holder.itemView.setLayoutParams(rvLayoutParams); + } else if (!checkLayoutParams(lp)) { + rvLayoutParams = (LayoutParams) generateLayoutParams(lp); + holder.itemView.setLayoutParams(rvLayoutParams); + } else { + rvLayoutParams = (LayoutParams) lp; + } + rvLayoutParams.mViewHolder = holder; + rvLayoutParams.mPendingInvalidate = fromScrap && bound; + return holder.itemView; + } + + private void attachAccessibilityDelegate(View itemView) { + if (mAccessibilityManager != null && mAccessibilityManager.isEnabled()) { + if (ViewCompat.getImportantForAccessibility(itemView) == + ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { + ViewCompat.setImportantForAccessibility(itemView, + ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES); + } + if (!ViewCompat.hasAccessibilityDelegate(itemView)) { + ViewCompat.setAccessibilityDelegate(itemView, + mAccessibilityDelegate.getItemDelegate()); + } + } + } + + private void invalidateDisplayListInt(ViewHolder holder) { + if (holder.itemView instanceof ViewGroup) { + invalidateDisplayListInt((ViewGroup) holder.itemView, false); + } + } + + private void invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis) { + for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) { + final View view = viewGroup.getChildAt(i); + if (view instanceof ViewGroup) { + invalidateDisplayListInt((ViewGroup) view, true); + } + } + if (!invalidateThis) { + return; + } + // we need to force it to become invisible + if (viewGroup.getVisibility() == View.INVISIBLE) { + viewGroup.setVisibility(View.VISIBLE); + viewGroup.setVisibility(View.INVISIBLE); + } else { + final int visibility = viewGroup.getVisibility(); + viewGroup.setVisibility(View.INVISIBLE); + viewGroup.setVisibility(visibility); + } + } + + /** + * Recycle a detached view. The specified view will be added to a pool of views + * for later rebinding and reuse. + * + *

A view must be fully detached (removed from parent) before it may be recycled. If the + * View is scrapped, it will be removed from scrap list.

+ * + * @param view Removed view for recycling + * @see LayoutManager#removeAndRecycleView(View, Recycler) + */ + public void recycleView(View view) { + // This public recycle method tries to make view recycle-able since layout manager + // intended to recycle this view (e.g. even if it is in scrap or change cache) + ViewHolder holder = getChildViewHolderInt(view); + if (holder.isTmpDetached()) { + removeDetachedView(view, false); + } + if (holder.isScrap()) { + holder.unScrap(); + } else if (holder.wasReturnedFromScrap()){ + holder.clearReturnedFromScrapFlag(); + } + recycleViewHolderInternal(holder); + } + + /** + * Internally, use this method instead of {@link #recycleView(android.view.View)} to + * catch potential bugs. + * @param view + */ + void recycleViewInternal(View view) { + recycleViewHolderInternal(getChildViewHolderInt(view)); + } + + void recycleAndClearCachedViews() { + final int count = mCachedViews.size(); + for (int i = count - 1; i >= 0; i--) { + recycleCachedViewAt(i); + } + mCachedViews.clear(); + } + + /** + * Recycles a cached view and removes the view from the list. Views are added to cache + * if and only if they are recyclable, so this method does not check it again. + *

+ * A small exception to this rule is when the view does not have an animator reference + * but transient state is true (due to animations created outside ItemAnimator). In that + * case, adapter may choose to recycle it. From RecyclerView's perspective, the view is + * still recyclable since Adapter wants to do so. + * + * @param cachedViewIndex The index of the view in cached views list + */ + void recycleCachedViewAt(int cachedViewIndex) { + if (DEBUG) { + Log.d(TAG, "Recycling cached view at index " + cachedViewIndex); + } + ViewHolder viewHolder = mCachedViews.get(cachedViewIndex); + if (DEBUG) { + Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder); + } + addViewHolderToRecycledViewPool(viewHolder); + mCachedViews.remove(cachedViewIndex); + } + + /** + * internal implementation checks if view is scrapped or attached and throws an exception + * if so. + * Public version un-scraps before calling recycle. + */ + void recycleViewHolderInternal(ViewHolder holder) { + if (holder.isScrap() || holder.itemView.getParent() != null) { + throw new IllegalArgumentException( + "Scrapped or attached views may not be recycled. isScrap:" + + holder.isScrap() + " isAttached:" + + (holder.itemView.getParent() != null)); + } + + if (holder.isTmpDetached()) { + throw new IllegalArgumentException("Tmp detached view should be removed " + + "from RecyclerView before it can be recycled: " + holder); + } + + if (holder.shouldIgnore()) { + throw new IllegalArgumentException("Trying to recycle an ignored view holder. You" + + " should first call stopIgnoringView(view) before calling recycle."); + } + //noinspection unchecked + final boolean transientStatePreventsRecycling = holder + .doesTransientStatePreventRecycling(); + final boolean forceRecycle = mAdapter != null + && transientStatePreventsRecycling + && mAdapter.onFailedToRecycleView(holder); + boolean cached = false; + boolean recycled = false; + if (forceRecycle || holder.isRecyclable()) { + if (!holder.isInvalid() && !holder.isRemoved() && !holder.isChanged()) { + // Retire oldest cached view + final int cachedViewSize = mCachedViews.size(); + if (cachedViewSize == mViewCacheMax && cachedViewSize > 0) { + recycleCachedViewAt(0); + } + if (cachedViewSize < mViewCacheMax) { + mCachedViews.add(holder); + cached = true; + } + } + if (!cached) { + addViewHolderToRecycledViewPool(holder); + recycled = true; + } + } else if (DEBUG) { + Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will " + + "re-visit here. We are still removing it from animation lists"); + } + // even if the holder is not removed, we still call this method so that it is removed + // from view holder lists. + mState.onViewRecycled(holder); + if (!cached && !recycled && transientStatePreventsRecycling) { + holder.mOwnerRecyclerView = null; + } + } + + void addViewHolderToRecycledViewPool(ViewHolder holder) { + ViewCompat.setAccessibilityDelegate(holder.itemView, null); + dispatchViewRecycled(holder); + holder.mOwnerRecyclerView = null; + getRecycledViewPool().putRecycledView(holder); + } + + /** + * Used as a fast path for unscrapping and recycling a view during a bulk operation. + * The caller must call {@link #clearScrap()} when it's done to update the recycler's + * internal bookkeeping. + */ + void quickRecycleScrapView(View view) { + final ViewHolder holder = getChildViewHolderInt(view); + holder.mScrapContainer = null; + holder.clearReturnedFromScrapFlag(); + recycleViewHolderInternal(holder); + } + + /** + * Mark an attached view as scrap. + * + *

"Scrap" views are still attached to their parent RecyclerView but are eligible + * for rebinding and reuse. Requests for a view for a given position may return a + * reused or rebound scrap view instance.

+ * + * @param view View to scrap + */ + void scrapView(View view) { + final ViewHolder holder = getChildViewHolderInt(view); + holder.setScrapContainer(this); + if (!holder.isChanged() || !supportsChangeAnimations()) { + if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) { + throw new IllegalArgumentException("Called scrap view with an invalid view." + + " Invalid views cannot be reused from scrap, they should rebound from" + + " recycler pool."); + } + mAttachedScrap.add(holder); + } else { + if (mChangedScrap == null) { + mChangedScrap = new ArrayList(); + } + mChangedScrap.add(holder); + } + } + + /** + * Remove a previously scrapped view from the pool of eligible scrap. + * + *

This view will no longer be eligible for reuse until re-scrapped or + * until it is explicitly removed and recycled.

+ */ + void unscrapView(ViewHolder holder) { + if (!holder.isChanged() || !supportsChangeAnimations() || mChangedScrap == null) { + mAttachedScrap.remove(holder); + } else { + mChangedScrap.remove(holder); + } + holder.mScrapContainer = null; + holder.clearReturnedFromScrapFlag(); + } + + int getScrapCount() { + return mAttachedScrap.size(); + } + + View getScrapViewAt(int index) { + return mAttachedScrap.get(index).itemView; + } + + void clearScrap() { + mAttachedScrap.clear(); + } + + ViewHolder getChangedScrapViewForPosition(int position) { + // If pre-layout, check the changed scrap for an exact match. + final int changedScrapSize; + if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) { + return null; + } + // find by position + for (int i = 0; i < changedScrapSize; i++) { + final ViewHolder holder = mChangedScrap.get(i); + if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) { + holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP); + return holder; + } + } + // find by id + if (mAdapter.hasStableIds()) { + final int offsetPosition = mAdapterHelper.findPositionOffset(position); + if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) { + final long id = mAdapter.getItemId(offsetPosition); + for (int i = 0; i < changedScrapSize; i++) { + final ViewHolder holder = mChangedScrap.get(i); + if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) { + holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP); + return holder; + } + } + } + } + return null; + } + + /** + * Returns a scrap view for the position. If type is not INVALID_TYPE, it also checks if + * ViewHolder's type matches the provided type. + * + * @param position Item position + * @param type View type + * @param dryRun Does a dry run, finds the ViewHolder but does not remove + * @return a ViewHolder that can be re-used for this position. + */ + ViewHolder getScrapViewForPosition(int position, int type, boolean dryRun) { + final int scrapCount = mAttachedScrap.size(); + + // Try first for an exact, non-invalid match from scrap. + for (int i = 0; i < scrapCount; i++) { + final ViewHolder holder = mAttachedScrap.get(i); + if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position + && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) { + if (type != INVALID_TYPE && holder.getItemViewType() != type) { + Log.e(TAG, "Scrap view for position " + position + " isn't dirty but has" + + " wrong view type! (found " + holder.getItemViewType() + + " but expected " + type + ")"); + break; + } + holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP); + return holder; + } + } + + if (!dryRun) { + View view = mChildHelper.findHiddenNonRemovedView(position, type); + if (view != null) { + // ending the animation should cause it to get recycled before we reuse it + mItemAnimator.endAnimation(getChildViewHolder(view)); + } + } + + // Search in our first-level recycled view cache. + final int cacheSize = mCachedViews.size(); + for (int i = 0; i < cacheSize; i++) { + final ViewHolder holder = mCachedViews.get(i); + // invalid view holders may be in cache if adapter has stable ids as they can be + // retrieved via getScrapViewForId + if (!holder.isInvalid() && holder.getLayoutPosition() == position) { + if (!dryRun) { + mCachedViews.remove(i); + } + if (DEBUG) { + Log.d(TAG, "getScrapViewForPosition(" + position + ", " + type + + ") found match in cache: " + holder); + } + return holder; + } + } + return null; + } + + ViewHolder getScrapViewForId(long id, int type, boolean dryRun) { + // Look in our attached views first + final int count = mAttachedScrap.size(); + for (int i = count - 1; i >= 0; i--) { + final ViewHolder holder = mAttachedScrap.get(i); + if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) { + if (type == holder.getItemViewType()) { + holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP); + if (holder.isRemoved()) { + // this might be valid in two cases: + // > item is removed but we are in pre-layout pass + // >> do nothing. return as is. make sure we don't rebind + // > item is removed then added to another position and we are in + // post layout. + // >> remove removed and invalid flags, add update flag to rebind + // because item was invisible to us and we don't know what happened in + // between. + if (!mState.isPreLayout()) { + holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE | + ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED); + } + } + return holder; + } else if (!dryRun) { + // Recycle this scrap. Type mismatch. + mAttachedScrap.remove(i); + removeDetachedView(holder.itemView, false); + quickRecycleScrapView(holder.itemView); + } + } + } + + // Search the first-level cache + final int cacheSize = mCachedViews.size(); + for (int i = cacheSize - 1; i >= 0; i--) { + final ViewHolder holder = mCachedViews.get(i); + if (holder.getItemId() == id) { + if (type == holder.getItemViewType()) { + if (!dryRun) { + mCachedViews.remove(i); + } + return holder; + } else if (!dryRun) { + recycleCachedViewAt(i); + } + } + } + return null; + } + + void dispatchViewRecycled(ViewHolder holder) { + if (mRecyclerListener != null) { + mRecyclerListener.onViewRecycled(holder); + } + if (mAdapter != null) { + mAdapter.onViewRecycled(holder); + } + if (mState != null) { + mState.onViewRecycled(holder); + } + if (DEBUG) Log.d(TAG, "dispatchViewRecycled: " + holder); + } + + void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter, + boolean compatibleWithPrevious) { + clear(); + getRecycledViewPool().onAdapterChanged(oldAdapter, newAdapter, compatibleWithPrevious); + } + + void offsetPositionRecordsForMove(int from, int to) { + final int start, end, inBetweenOffset; + if (from < to) { + start = from; + end = to; + inBetweenOffset = -1; + } else { + start = to; + end = from; + inBetweenOffset = 1; + } + final int cachedCount = mCachedViews.size(); + for (int i = 0; i < cachedCount; i++) { + final ViewHolder holder = mCachedViews.get(i); + if (holder == null || holder.mPosition < start || holder.mPosition > end) { + continue; + } + if (holder.mPosition == from) { + holder.offsetPosition(to - from, false); + } else { + holder.offsetPosition(inBetweenOffset, false); + } + if (DEBUG) { + Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder " + + holder); + } + } + } + + void offsetPositionRecordsForInsert(int insertedAt, int count) { + final int cachedCount = mCachedViews.size(); + for (int i = 0; i < cachedCount; i++) { + final ViewHolder holder = mCachedViews.get(i); + if (holder != null && holder.getLayoutPosition() >= insertedAt) { + if (DEBUG) { + Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder " + + holder + " now at position " + (holder.mPosition + count)); + } + holder.offsetPosition(count, true); + } + } + } + + /** + * @param removedFrom Remove start index + * @param count Remove count + * @param applyToPreLayout If true, changes will affect ViewHolder's pre-layout position, if + * false, they'll be applied before the second layout pass + */ + void offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout) { + final int removedEnd = removedFrom + count; + final int cachedCount = mCachedViews.size(); + for (int i = cachedCount - 1; i >= 0; i--) { + final ViewHolder holder = mCachedViews.get(i); + if (holder != null) { + if (holder.getLayoutPosition() >= removedEnd) { + if (DEBUG) { + Log.d(TAG, "offsetPositionRecordsForRemove cached " + i + + " holder " + holder + " now at position " + + (holder.mPosition - count)); + } + holder.offsetPosition(-count, applyToPreLayout); + } else if (holder.getLayoutPosition() >= removedFrom) { + // Item for this view was removed. Dump it from the cache. + holder.addFlags(ViewHolder.FLAG_REMOVED); + recycleCachedViewAt(i); + } + } + } + } + + void setViewCacheExtension(ViewCacheExtension extension) { + mViewCacheExtension = extension; + } + + void setRecycledViewPool(RecycledViewPool pool) { + if (mRecyclerPool != null) { + mRecyclerPool.detach(); + } + mRecyclerPool = pool; + if (pool != null) { + mRecyclerPool.attach(getAdapter()); + } + } + + RecycledViewPool getRecycledViewPool() { + if (mRecyclerPool == null) { + mRecyclerPool = new RecycledViewPool(); + } + return mRecyclerPool; + } + + void viewRangeUpdate(int positionStart, int itemCount) { + final int positionEnd = positionStart + itemCount; + final int cachedCount = mCachedViews.size(); + for (int i = 0; i < cachedCount; i++) { + final ViewHolder holder = mCachedViews.get(i); + if (holder == null) { + continue; + } + + final int pos = holder.getLayoutPosition(); + if (pos >= positionStart && pos < positionEnd) { + holder.addFlags(ViewHolder.FLAG_UPDATE); + // cached views should not be flagged as changed because this will cause them + // to animate when they are returned from cache. + } + } + } + + void setAdapterPositionsAsUnknown() { + final int cachedCount = mCachedViews.size(); + for (int i = 0; i < cachedCount; i++) { + final ViewHolder holder = mCachedViews.get(i); + if (holder != null) { + holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN); + } + } + } + + void markKnownViewsInvalid() { + if (mAdapter != null && mAdapter.hasStableIds()) { + final int cachedCount = mCachedViews.size(); + for (int i = 0; i < cachedCount; i++) { + final ViewHolder holder = mCachedViews.get(i); + if (holder != null) { + holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID); + } + } + } else { + // we cannot re-use cached views in this case. Recycle them all + recycleAndClearCachedViews(); + } + } + + void clearOldPositions() { + final int cachedCount = mCachedViews.size(); + for (int i = 0; i < cachedCount; i++) { + final ViewHolder holder = mCachedViews.get(i); + holder.clearOldPosition(); + } + final int scrapCount = mAttachedScrap.size(); + for (int i = 0; i < scrapCount; i++) { + mAttachedScrap.get(i).clearOldPosition(); + } + if (mChangedScrap != null) { + final int changedScrapCount = mChangedScrap.size(); + for (int i = 0; i < changedScrapCount; i++) { + mChangedScrap.get(i).clearOldPosition(); + } + } + } + + void markItemDecorInsetsDirty() { + final int cachedCount = mCachedViews.size(); + for (int i = 0; i < cachedCount; i++) { + final ViewHolder holder = mCachedViews.get(i); + LayoutParams layoutParams = (LayoutParams) holder.itemView.getLayoutParams(); + if (layoutParams != null) { + layoutParams.mInsetsDirty = true; + } + } + } + } + + /** + * ViewCacheExtension is a helper class to provide an additional layer of view caching that can + * ben controlled by the developer. + *

+ * When {@link Recycler#getViewForPosition(int)} is called, Recycler checks attached scrap and + * first level cache to find a matching View. If it cannot find a suitable View, Recycler will + * call the {@link #getViewForPositionAndType(Recycler, int, int)} before checking + * {@link RecycledViewPool}. + *

+ * Note that, Recycler never sends Views to this method to be cached. It is developers + * responsibility to decide whether they want to keep their Views in this custom cache or let + * the default recycling policy handle it. + */ + public abstract static class ViewCacheExtension { + + /** + * Returns a View that can be binded to the given Adapter position. + *

+ * This method should not create a new View. Instead, it is expected to return + * an already created View that can be re-used for the given type and position. + * If the View is marked as ignored, it should first call + * {@link LayoutManager#stopIgnoringView(View)} before returning the View. + *

+ * RecyclerView will re-bind the returned View to the position if necessary. + * + * @param recycler The Recycler that can be used to bind the View + * @param position The adapter position + * @param type The type of the View, defined by adapter + * @return A View that is bound to the given position or NULL if there is no View to re-use + * @see LayoutManager#ignoreView(View) + */ + abstract public View getViewForPositionAndType(Recycler recycler, int position, int type); + } + + /** + * Base class for an Adapter + * + *

Adapters provide a binding from an app-specific data set to views that are displayed + * within a {@link RecyclerView}.

+ */ + public static abstract class Adapter { + private final AdapterDataObservable mObservable = new AdapterDataObservable(); + private boolean mHasStableIds = false; + + /** + * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent + * an item. + *

+ * This new ViewHolder should be constructed with a new View that can represent the items + * of the given type. You can either create a new View manually or inflate it from an XML + * layout file. + *

+ * The new ViewHolder will be used to display items of the adapter using + * {@link #onBindViewHolder(ViewHolder, int)}. Since it will be re-used to display different + * items in the data set, it is a good idea to cache references to sub views of the View to + * avoid unnecessary {@link View#findViewById(int)} calls. + * + * @param parent The ViewGroup into which the new View will be added after it is bound to + * an adapter position. + * @param viewType The view type of the new View. + * + * @return A new ViewHolder that holds a View of the given view type. + * @see #getItemViewType(int) + * @see #onBindViewHolder(ViewHolder, int) + */ + public abstract VH onCreateViewHolder(ViewGroup parent, int viewType); + + /** + * Called by RecyclerView to display the data at the specified position. This method + * should update the contents of the {@link ViewHolder#itemView} to reflect the item at + * the given position. + *

+ * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this + * method again if the position of the item changes in the data set unless the item itself + * is invalidated or the new position cannot be determined. For this reason, you should only + * use the position parameter while acquiring the related data item inside this + * method and should not keep a copy of it. If you need the position of an item later on + * (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will have + * the updated adapter position. + * + * @param holder The ViewHolder which should be updated to represent the contents of the + * item at the given position in the data set. + * @param position The position of the item within the adapter's data set. + */ + public abstract void onBindViewHolder(VH holder, int position); + + /** + * This method calls {@link #onCreateViewHolder(ViewGroup, int)} to create a new + * {@link ViewHolder} and initializes some private fields to be used by RecyclerView. + * + * @see #onCreateViewHolder(ViewGroup, int) + */ + public final VH createViewHolder(ViewGroup parent, int viewType) { + final VH holder = onCreateViewHolder(parent, viewType); + holder.mItemViewType = viewType; + return holder; + } + + /** + * This method internally calls {@link #onBindViewHolder(ViewHolder, int)} to update the + * {@link ViewHolder} contents with the item at the given position and also sets up some + * private fields to be used by RecyclerView. + * + * @see #onBindViewHolder(ViewHolder, int) + */ + public final void bindViewHolder(VH holder, int position) { + holder.mPosition = position; + if (hasStableIds()) { + holder.mItemId = getItemId(position); + } + holder.setFlags(ViewHolder.FLAG_BOUND, + ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID + | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN); + onBindViewHolder(holder, position); + } + + /** + * Return the view type of the item at position for the purposes + * of view recycling. + * + *

The default implementation of this method returns 0, making the assumption of + * a single view type for the adapter. Unlike ListView adapters, types need not + * be contiguous. Consider using id resources to uniquely identify item view types. + * + * @param position position to query + * @return integer value identifying the type of the view needed to represent the item at + * position. Type codes need not be contiguous. + */ + public int getItemViewType(int position) { + return 0; + } + + /** + * Indicates whether each item in the data set can be represented with a unique identifier + * of type {@link java.lang.Long}. + * + * @param hasStableIds Whether items in data set have unique identifiers or not. + * @see #hasStableIds() + * @see #getItemId(int) + */ + public void setHasStableIds(boolean hasStableIds) { + if (hasObservers()) { + throw new IllegalStateException("Cannot change whether this adapter has " + + "stable IDs while the adapter has registered observers."); + } + mHasStableIds = hasStableIds; + } + + /** + * Return the stable ID for the item at position. If {@link #hasStableIds()} + * would return false this method should return {@link #NO_ID}. The default implementation + * of this method returns {@link #NO_ID}. + * + * @param position Adapter position to query + * @return the stable ID of the item at position + */ + public long getItemId(int position) { + return NO_ID; + } + + /** + * Returns the total number of items in the data set hold by the adapter. + * + * @return The total number of items in this adapter. + */ + public abstract int getItemCount(); + + /** + * Returns true if this adapter publishes a unique long value that can + * act as a key for the item at a given position in the data set. If that item is relocated + * in the data set, the ID returned for that item should be the same. + * + * @return true if this adapter's items have stable IDs + */ + public final boolean hasStableIds() { + return mHasStableIds; + } + + /** + * Called when a view created by this adapter has been recycled. + * + *

A view is recycled when a {@link LayoutManager} decides that it no longer + * needs to be attached to its parent {@link RecyclerView}. This can be because it has + * fallen out of visibility or a set of cached views represented by views still + * attached to the parent RecyclerView. If an item view has large or expensive data + * bound to it such as large bitmaps, this may be a good place to release those + * resources.

+ *

+ * RecyclerView calls this method right before clearing ViewHolder's internal data and + * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information + * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get + * its adapter position. + * + * @param holder The ViewHolder for the view being recycled + */ + public void onViewRecycled(VH holder) { + } + + /** + * Called by the RecyclerView if a ViewHolder created by this Adapter cannot be recycled + * due to its transient state. Upon receiving this callback, Adapter can clear the + * animation(s) that effect the View's transient state and return true so that + * the View can be recycled. Keep in mind that the View in question is already removed from + * the RecyclerView. + *

+ * In some cases, it is acceptable to recycle a View although it has transient state. Most + * of the time, this is a case where the transient state will be cleared in + * {@link #onBindViewHolder(ViewHolder, int)} call when View is rebound to a new position. + * For this reason, RecyclerView leaves the decision to the Adapter and uses the return + * value of this method to decide whether the View should be recycled or not. + *

+ * Note that when all animations are created by {@link RecyclerView.ItemAnimator}, you + * should never receive this callback because RecyclerView keeps those Views as children + * until their animations are complete. This callback is useful when children of the item + * views create animations which may not be easy to implement using an {@link ItemAnimator}. + *

+ * You should never fix this issue by calling + * holder.itemView.setHasTransientState(false); unless you've previously called + * holder.itemView.setHasTransientState(true);. Each + * View.setHasTransientState(true) call must be matched by a + * View.setHasTransientState(false) call, otherwise, the state of the View + * may become inconsistent. You should always prefer to end or cancel animations that are + * triggering the transient state instead of handling it manually. + * + * @param holder The ViewHolder containing the View that could not be recycled due to its + * transient state. + * @return True if the View should be recycled, false otherwise. Note that if this method + * returns true, RecyclerView will ignore the transient state of + * the View and recycle it regardless. If this method returns false, + * RecyclerView will check the View's transient state again before giving a final decision. + * Default implementation returns false. + */ + public boolean onFailedToRecycleView(VH holder) { + return false; + } + + /** + * Called when a view created by this adapter has been attached to a window. + * + *

This can be used as a reasonable signal that the view is about to be seen + * by the user. If the adapter previously freed any resources in + * {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow} + * those resources should be restored here.

+ * + * @param holder Holder of the view being attached + */ + public void onViewAttachedToWindow(VH holder) { + } + + /** + * Called when a view created by this adapter has been detached from its window. + * + *

Becoming detached from the window is not necessarily a permanent condition; + * the consumer of an Adapter's views may choose to cache views offscreen while they + * are not visible, attaching an detaching them as appropriate.

+ * + * @param holder Holder of the view being detached + */ + public void onViewDetachedFromWindow(VH holder) { + } + + /** + * Returns true if one or more observers are attached to this adapter. + * + * @return true if this adapter has observers + */ + public final boolean hasObservers() { + return mObservable.hasObservers(); + } + + /** + * Register a new observer to listen for data changes. + * + *

The adapter may publish a variety of events describing specific changes. + * Not all adapters may support all change types and some may fall back to a generic + * {@link android.support.v7.widget.RecyclerView.AdapterDataObserver#onChanged() + * "something changed"} event if more specific data is not available.

+ * + *

Components registering observers with an adapter are responsible for + * {@link #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver) + * unregistering} those observers when finished.

+ * + * @param observer Observer to register + * + * @see #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver) + */ + public void registerAdapterDataObserver(AdapterDataObserver observer) { + mObservable.registerObserver(observer); + } + + /** + * Unregister an observer currently listening for data changes. + * + *

The unregistered observer will no longer receive events about changes + * to the adapter.

+ * + * @param observer Observer to unregister + * + * @see #registerAdapterDataObserver(RecyclerView.AdapterDataObserver) + */ + public void unregisterAdapterDataObserver(AdapterDataObserver observer) { + mObservable.unregisterObserver(observer); + } + + /** + * Called by RecyclerView when it starts observing this Adapter. + *

+ * Keep in mind that same adapter may be observed by multiple RecyclerViews. + * + * @param recyclerView The RecyclerView instance which started observing this adapter. + * @see #onDetachedFromRecyclerView(RecyclerView) + */ + public void onAttachedToRecyclerView(RecyclerView recyclerView) { + } + + /** + * Called by RecyclerView when it stops observing this Adapter. + * + * @param recyclerView The RecyclerView instance which stopped observing this adapter. + * @see #onAttachedToRecyclerView(RecyclerView) + */ + public void onDetachedFromRecyclerView(RecyclerView recyclerView) { + } + + /** + * Notify any registered observers that the data set has changed. + * + *

There are two different classes of data change events, item changes and structural + * changes. Item changes are when a single item has its data updated but no positional + * changes have occurred. Structural changes are when items are inserted, removed or moved + * within the data set.

+ * + *

This event does not specify what about the data set has changed, forcing + * any observers to assume that all existing items and structure may no longer be valid. + * LayoutManagers will be forced to fully rebind and relayout all visible views.

+ * + *

RecyclerView will attempt to synthesize visible structural change events + * for adapters that report that they have {@link #hasStableIds() stable IDs} when + * this method is used. This can help for the purposes of animation and visual + * object persistence but individual item views will still need to be rebound + * and relaid out.

+ * + *

If you are writing an adapter it will always be more efficient to use the more + * specific change events if you can. Rely on notifyDataSetChanged() + * as a last resort.

+ * + * @see #notifyItemChanged(int) + * @see #notifyItemInserted(int) + * @see #notifyItemRemoved(int) + * @see #notifyItemRangeChanged(int, int) + * @see #notifyItemRangeInserted(int, int) + * @see #notifyItemRangeRemoved(int, int) + */ + public final void notifyDataSetChanged() { + mObservable.notifyChanged(); + } + + /** + * Notify any registered observers that the item at position has changed. + * + *

This is an item change event, not a structural change event. It indicates that any + * reflection of the data at position is out of date and should be updated. + * The item at position retains the same identity.

+ * + * @param position Position of the item that has changed + * + * @see #notifyItemRangeChanged(int, int) + */ + public final void notifyItemChanged(int position) { + mObservable.notifyItemRangeChanged(position, 1); + } + + /** + * Notify any registered observers that the itemCount items starting at + * position positionStart have changed. + * + *

This is an item change event, not a structural change event. It indicates that + * any reflection of the data in the given position range is out of date and should + * be updated. The items in the given range retain the same identity.

+ * + * @param positionStart Position of the first item that has changed + * @param itemCount Number of items that have changed + * + * @see #notifyItemChanged(int) + */ + public final void notifyItemRangeChanged(int positionStart, int itemCount) { + mObservable.notifyItemRangeChanged(positionStart, itemCount); + } + + /** + * Notify any registered observers that the item reflected at position + * has been newly inserted. The item previously at position is now at + * position position + 1. + * + *

This is a structural change event. Representations of other existing items in the + * data set are still considered up to date and will not be rebound, though their + * positions may be altered.

+ * + * @param position Position of the newly inserted item in the data set + * + * @see #notifyItemRangeInserted(int, int) + */ + public final void notifyItemInserted(int position) { + mObservable.notifyItemRangeInserted(position, 1); + } + + /** + * Notify any registered observers that the item reflected at fromPosition + * has been moved to toPosition. + * + *

This is a structural change event. Representations of other existing items in the + * data set are still considered up to date and will not be rebound, though their + * positions may be altered.

+ * + * @param fromPosition Previous position of the item. + * @param toPosition New position of the item. + */ + public final void notifyItemMoved(int fromPosition, int toPosition) { + mObservable.notifyItemMoved(fromPosition, toPosition); + } + + /** + * Notify any registered observers that the currently reflected itemCount + * items starting at positionStart have been newly inserted. The items + * previously located at positionStart and beyond can now be found starting + * at position positionStart + itemCount. + * + *

This is a structural change event. Representations of other existing items in the + * data set are still considered up to date and will not be rebound, though their positions + * may be altered.

+ * + * @param positionStart Position of the first item that was inserted + * @param itemCount Number of items inserted + * + * @see #notifyItemInserted(int) + */ + public final void notifyItemRangeInserted(int positionStart, int itemCount) { + mObservable.notifyItemRangeInserted(positionStart, itemCount); + } + + /** + * Notify any registered observers that the item previously located at position + * has been removed from the data set. The items previously located at and after + * position may now be found at oldPosition - 1. + * + *

This is a structural change event. Representations of other existing items in the + * data set are still considered up to date and will not be rebound, though their positions + * may be altered.

+ * + * @param position Position of the item that has now been removed + * + * @see #notifyItemRangeRemoved(int, int) + */ + public final void notifyItemRemoved(int position) { + mObservable.notifyItemRangeRemoved(position, 1); + } + + /** + * Notify any registered observers that the itemCount items previously + * located at positionStart have been removed from the data set. The items + * previously located at and after positionStart + itemCount may now be found + * at oldPosition - itemCount. + * + *

This is a structural change event. Representations of other existing items in the data + * set are still considered up to date and will not be rebound, though their positions + * may be altered.

+ * + * @param positionStart Previous position of the first item that was removed + * @param itemCount Number of items removed from the data set + */ + public final void notifyItemRangeRemoved(int positionStart, int itemCount) { + mObservable.notifyItemRangeRemoved(positionStart, itemCount); + } + } + + private void dispatchChildDetached(View child) { + if (mAdapter != null) { + mAdapter.onViewDetachedFromWindow(getChildViewHolderInt(child)); + } + onChildDetachedFromWindow(child); + } + + private void dispatchChildAttached(View child) { + if (mAdapter != null) { + mAdapter.onViewAttachedToWindow(getChildViewHolderInt(child)); + } + onChildAttachedToWindow(child); + } + + /** + * A LayoutManager is responsible for measuring and positioning item views + * within a RecyclerView as well as determining the policy for when to recycle + * item views that are no longer visible to the user. By changing the LayoutManager + * a RecyclerView can be used to implement a standard vertically scrolling list, + * a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock + * layout managers are provided for general use. + */ + public static abstract class LayoutManager { + ChildHelper mChildHelper; + RecyclerView mRecyclerView; + + @Nullable + SmoothScroller mSmoothScroller; + + private boolean mRequestedSimpleAnimations = false; + + private boolean mIsAttachedToWindow = false; + + void setRecyclerView(RecyclerView recyclerView) { + if (recyclerView == null) { + mRecyclerView = null; + mChildHelper = null; + } else { + mRecyclerView = recyclerView; + mChildHelper = recyclerView.mChildHelper; + } + + } + + /** + * Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView + */ + public void requestLayout() { + if(mRecyclerView != null) { + mRecyclerView.requestLayout(); + } + } + + /** + * Checks if RecyclerView is in the middle of a layout or scroll and throws an + * {@link IllegalStateException} if it is not. + * + * @param message The message for the exception. Can be null. + * @see #assertNotInLayoutOrScroll(String) + */ + public void assertInLayoutOrScroll(String message) { + if (mRecyclerView != null) { + mRecyclerView.assertInLayoutOrScroll(message); + } + } + + /** + * Checks if RecyclerView is in the middle of a layout or scroll and throws an + * {@link IllegalStateException} if it is. + * + * @param message The message for the exception. Can be null. + * @see #assertInLayoutOrScroll(String) + */ + public void assertNotInLayoutOrScroll(String message) { + if (mRecyclerView != null) { + mRecyclerView.assertNotInLayoutOrScroll(message); + } + } + + /** + * Returns whether this LayoutManager supports automatic item animations. + * A LayoutManager wishing to support item animations should obey certain + * rules as outlined in {@link #onLayoutChildren(Recycler, State)}. + * The default return value is false, so subclasses of LayoutManager + * will not get predictive item animations by default. + * + *

Whether item animations are enabled in a RecyclerView is determined both + * by the return value from this method and the + * {@link RecyclerView#setItemAnimator(ItemAnimator) ItemAnimator} set on the + * RecyclerView itself. If the RecyclerView has a non-null ItemAnimator but this + * method returns false, then simple item animations will be enabled, in which + * views that are moving onto or off of the screen are simply faded in/out. If + * the RecyclerView has a non-null ItemAnimator and this method returns true, + * then there will be two calls to {@link #onLayoutChildren(Recycler, State)} to + * setup up the information needed to more intelligently predict where appearing + * and disappearing views should be animated from/to.

+ * + * @return true if predictive item animations should be enabled, false otherwise + */ + public boolean supportsPredictiveItemAnimations() { + return false; + } + + void dispatchAttachedToWindow(RecyclerView view) { + mIsAttachedToWindow = true; + onAttachedToWindow(view); + } + + void dispatchDetachedFromWindow(RecyclerView view, Recycler recycler) { + mIsAttachedToWindow = false; + onDetachedFromWindow(view, recycler); + } + + /** + * Returns whether LayoutManager is currently attached to a RecyclerView which is attached + * to a window. + * + * @return True if this LayoutManager is controlling a RecyclerView and the RecyclerView + * is attached to window. + */ + public boolean isAttachedToWindow() { + return mIsAttachedToWindow; + } + + /** + * Causes the Runnable to execute on the next animation time step. + * The runnable will be run on the user interface thread. + *

+ * Calling this method when LayoutManager is not attached to a RecyclerView has no effect. + * + * @param action The Runnable that will be executed. + * + * @see #removeCallbacks + */ + public void postOnAnimation(Runnable action) { + if (mRecyclerView != null) { + ViewCompat.postOnAnimation(mRecyclerView, action); + } + } + + /** + * Removes the specified Runnable from the message queue. + *

+ * Calling this method when LayoutManager is not attached to a RecyclerView has no effect. + * + * @param action The Runnable to remove from the message handling queue + * + * @return true if RecyclerView could ask the Handler to remove the Runnable, + * false otherwise. When the returned value is true, the Runnable + * may or may not have been actually removed from the message queue + * (for instance, if the Runnable was not in the queue already.) + * + * @see #postOnAnimation + */ + public boolean removeCallbacks(Runnable action) { + if (mRecyclerView != null) { + return mRecyclerView.removeCallbacks(action); + } + return false; + } + /** + * Called when this LayoutManager is both attached to a RecyclerView and that RecyclerView + * is attached to a window. + * + *

Subclass implementations should always call through to the superclass implementation. + *

+ * + * @param view The RecyclerView this LayoutManager is bound to + */ + public void onAttachedToWindow(RecyclerView view) { + } + + /** + * @deprecated + * override {@link #onDetachedFromWindow(RecyclerView, Recycler)} + */ + @Deprecated + public void onDetachedFromWindow(RecyclerView view) { + + } + + /** + * Called when this LayoutManager is detached from its parent RecyclerView or when + * its parent RecyclerView is detached from its window. + * + *

Subclass implementations should always call through to the superclass implementation. + *

+ * + * @param view The RecyclerView this LayoutManager is bound to + * @param recycler The recycler to use if you prefer to recycle your children instead of + * keeping them around. + */ + public void onDetachedFromWindow(RecyclerView view, Recycler recycler) { + onDetachedFromWindow(view); + } + + /** + * Check if the RecyclerView is configured to clip child views to its padding. + * + * @return true if this RecyclerView clips children to its padding, false otherwise + */ + public boolean getClipToPadding() { + return mRecyclerView != null && mRecyclerView.mClipToPadding; + } + + /** + * Lay out all relevant child views from the given adapter. + * + * The LayoutManager is in charge of the behavior of item animations. By default, + * RecyclerView has a non-null {@link #getItemAnimator() ItemAnimator}, and simple + * item animations are enabled. This means that add/remove operations on the + * adapter will result in animations to add new or appearing items, removed or + * disappearing items, and moved items. If a LayoutManager returns false from + * {@link #supportsPredictiveItemAnimations()}, which is the default, and runs a + * normal layout operation during {@link #onLayoutChildren(Recycler, State)}, the + * RecyclerView will have enough information to run those animations in a simple + * way. For example, the default ItemAnimator, {@link DefaultItemAnimator}, will + * simple fade views in and out, whether they are actuall added/removed or whether + * they are moved on or off the screen due to other add/remove operations. + * + *

A LayoutManager wanting a better item animation experience, where items can be + * animated onto and off of the screen according to where the items exist when they + * are not on screen, then the LayoutManager should return true from + * {@link #supportsPredictiveItemAnimations()} and add additional logic to + * {@link #onLayoutChildren(Recycler, State)}. Supporting predictive animations + * means that {@link #onLayoutChildren(Recycler, State)} will be called twice; + * once as a "pre" layout step to determine where items would have been prior to + * a real layout, and again to do the "real" layout. In the pre-layout phase, + * items will remember their pre-layout positions to allow them to be laid out + * appropriately. Also, {@link LayoutParams#isItemRemoved() removed} items will + * be returned from the scrap to help determine correct placement of other items. + * These removed items should not be added to the child list, but should be used + * to help calculate correct positioning of other views, including views that + * were not previously onscreen (referred to as APPEARING views), but whose + * pre-layout offscreen position can be determined given the extra + * information about the pre-layout removed views.

+ * + *

The second layout pass is the real layout in which only non-removed views + * will be used. The only additional requirement during this pass is, if + * {@link #supportsPredictiveItemAnimations()} returns true, to note which + * views exist in the child list prior to layout and which are not there after + * layout (referred to as DISAPPEARING views), and to position/layout those views + * appropriately, without regard to the actual bounds of the RecyclerView. This allows + * the animation system to know the location to which to animate these disappearing + * views.

+ * + *

The default LayoutManager implementations for RecyclerView handle all of these + * requirements for animations already. Clients of RecyclerView can either use one + * of these layout managers directly or look at their implementations of + * onLayoutChildren() to see how they account for the APPEARING and + * DISAPPEARING views.

+ * + * @param recycler Recycler to use for fetching potentially cached views for a + * position + * @param state Transient state of RecyclerView + */ + public void onLayoutChildren(Recycler recycler, State state) { + Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) "); + } + + /** + * Create a default LayoutParams object for a child of the RecyclerView. + * + *

LayoutManagers will often want to use a custom LayoutParams type + * to store extra information specific to the layout. Client code should subclass + * {@link RecyclerView.LayoutParams} for this purpose.

+ * + *

Important: if you use your own custom LayoutParams type + * you must also override + * {@link #checkLayoutParams(LayoutParams)}, + * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and + * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.

+ * + * @return A new LayoutParams for a child view + */ + public abstract LayoutParams generateDefaultLayoutParams(); + + /** + * Determines the validity of the supplied LayoutParams object. + * + *

This should check to make sure that the object is of the correct type + * and all values are within acceptable ranges. The default implementation + * returns true for non-null params.

+ * + * @param lp LayoutParams object to check + * @return true if this LayoutParams object is valid, false otherwise + */ + public boolean checkLayoutParams(LayoutParams lp) { + return lp != null; + } + + /** + * Create a LayoutParams object suitable for this LayoutManager, copying relevant + * values from the supplied LayoutParams object if possible. + * + *

Important: if you use your own custom LayoutParams type + * you must also override + * {@link #checkLayoutParams(LayoutParams)}, + * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and + * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.

+ * + * @param lp Source LayoutParams object to copy values from + * @return a new LayoutParams object + */ + public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { + if (lp instanceof LayoutParams) { + return new LayoutParams((LayoutParams) lp); + } else if (lp instanceof MarginLayoutParams) { + return new LayoutParams((MarginLayoutParams) lp); + } else { + return new LayoutParams(lp); + } + } + + /** + * Create a LayoutParams object suitable for this LayoutManager from + * an inflated layout resource. + * + *

Important: if you use your own custom LayoutParams type + * you must also override + * {@link #checkLayoutParams(LayoutParams)}, + * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and + * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.

+ * + * @param c Context for obtaining styled attributes + * @param attrs AttributeSet describing the supplied arguments + * @return a new LayoutParams object + */ + public LayoutParams generateLayoutParams(Context c, AttributeSet attrs) { + return new LayoutParams(c, attrs); + } + + /** + * Scroll horizontally by dx pixels in screen coordinates and return the distance traveled. + * The default implementation does nothing and returns 0. + * + * @param dx distance to scroll by in pixels. X increases as scroll position + * approaches the right. + * @param recycler Recycler to use for fetching potentially cached views for a + * position + * @param state Transient state of RecyclerView + * @return The actual distance scrolled. The return value will be negative if dx was + * negative and scrolling proceeeded in that direction. + * Math.abs(result) may be less than dx if a boundary was reached. + */ + public int scrollHorizontallyBy(int dx, Recycler recycler, State state) { + return 0; + } + + /** + * Scroll vertically by dy pixels in screen coordinates and return the distance traveled. + * The default implementation does nothing and returns 0. + * + * @param dy distance to scroll in pixels. Y increases as scroll position + * approaches the bottom. + * @param recycler Recycler to use for fetching potentially cached views for a + * position + * @param state Transient state of RecyclerView + * @return The actual distance scrolled. The return value will be negative if dy was + * negative and scrolling proceeeded in that direction. + * Math.abs(result) may be less than dy if a boundary was reached. + */ + public int scrollVerticallyBy(int dy, Recycler recycler, State state) { + return 0; + } + + /** + * Query if horizontal scrolling is currently supported. The default implementation + * returns false. + * + * @return True if this LayoutManager can scroll the current contents horizontally + */ + public boolean canScrollHorizontally() { + return false; + } + + /** + * Query if vertical scrolling is currently supported. The default implementation + * returns false. + * + * @return True if this LayoutManager can scroll the current contents vertically + */ + public boolean canScrollVertically() { + return false; + } + + /** + * Scroll to the specified adapter position. + * + * Actual position of the item on the screen depends on the LayoutManager implementation. + * @param position Scroll to this adapter position. + */ + public void scrollToPosition(int position) { + if (DEBUG) { + Log.e(TAG, "You MUST implement scrollToPosition. It will soon become abstract"); + } + } + + /** + *

Smooth scroll to the specified adapter position.

+ *

To support smooth scrolling, override this method, create your {@link SmoothScroller} + * instance and call {@link #startSmoothScroll(SmoothScroller)}. + *

+ * @param recyclerView The RecyclerView to which this layout manager is attached + * @param state Current State of RecyclerView + * @param position Scroll to this adapter position. + */ + public void smoothScrollToPosition(RecyclerView recyclerView, State state, + int position) { + Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling"); + } + + /** + *

Starts a smooth scroll using the provided SmoothScroller.

+ *

Calling this method will cancel any previous smooth scroll request.

+ * @param smoothScroller Unstance which defines how smooth scroll should be animated + */ + public void startSmoothScroll(SmoothScroller smoothScroller) { + if (mSmoothScroller != null && smoothScroller != mSmoothScroller + && mSmoothScroller.isRunning()) { + mSmoothScroller.stop(); + } + mSmoothScroller = smoothScroller; + mSmoothScroller.start(mRecyclerView, this); + } + + /** + * @return true if RecycylerView is currently in the state of smooth scrolling. + */ + public boolean isSmoothScrolling() { + return mSmoothScroller != null && mSmoothScroller.isRunning(); + } + + + /** + * Returns the resolved layout direction for this RecyclerView. + * + * @return {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_RTL} if the layout + * direction is RTL or returns + * {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_LTR} if the layout direction + * is not RTL. + */ + public int getLayoutDirection() { + return ViewCompat.getLayoutDirection(mRecyclerView); + } + + /** + * Ends all animations on the view created by the {@link ItemAnimator}. + * + * @param view The View for which the animations should be ended. + * @see RecyclerView.ItemAnimator#endAnimations() + */ + public void endAnimation(View view) { + if (mRecyclerView.mItemAnimator != null) { + mRecyclerView.mItemAnimator.endAnimation(getChildViewHolderInt(view)); + } + } + + /** + * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view + * to the layout that is known to be going away, either because it has been + * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the + * visible portion of the container but is being laid out in order to inform RecyclerView + * in how to animate the item out of view. + *

+ * Views added via this method are going to be invisible to LayoutManager after the + * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)} + * or won't be included in {@link #getChildCount()} method. + * + * @param child View to add and then remove with animation. + */ + public void addDisappearingView(View child) { + addDisappearingView(child, -1); + } + + /** + * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view + * to the layout that is known to be going away, either because it has been + * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the + * visible portion of the container but is being laid out in order to inform RecyclerView + * in how to animate the item out of view. + *

+ * Views added via this method are going to be invisible to LayoutManager after the + * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)} + * or won't be included in {@link #getChildCount()} method. + * + * @param child View to add and then remove with animation. + * @param index Index of the view. + */ + public void addDisappearingView(View child, int index) { + addViewInt(child, index, true); + } + + /** + * Add a view to the currently attached RecyclerView if needed. LayoutManagers should + * use this method to add views obtained from a {@link Recycler} using + * {@link Recycler#getViewForPosition(int)}. + * + * @param child View to add + */ + public void addView(View child) { + addView(child, -1); + } + + /** + * Add a view to the currently attached RecyclerView if needed. LayoutManagers should + * use this method to add views obtained from a {@link Recycler} using + * {@link Recycler#getViewForPosition(int)}. + * + * @param child View to add + * @param index Index to add child at + */ + public void addView(View child, int index) { + addViewInt(child, index, false); + } + + private void addViewInt(View child, int index, boolean disappearing) { + final ViewHolder holder = getChildViewHolderInt(child); + if (disappearing || holder.isRemoved()) { + // these views will be hidden at the end of the layout pass. + mRecyclerView.mState.addToDisappearingList(child); + } else { + // This may look like unnecessary but may happen if layout manager supports + // predictive layouts and adapter removed then re-added the same item. + // In this case, added version will be visible in the post layout (because add is + // deferred) but RV will still bind it to the same View. + // So if a View re-appears in post layout pass, remove it from disappearing list. + mRecyclerView.mState.removeFromDisappearingList(child); + } + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (holder.wasReturnedFromScrap() || holder.isScrap()) { + if (holder.isScrap()) { + holder.unScrap(); + } else { + holder.clearReturnedFromScrapFlag(); + } + mChildHelper.attachViewToParent(child, index, child.getLayoutParams(), false); + if (DISPATCH_TEMP_DETACH) { + ViewCompat.dispatchFinishTemporaryDetach(child); + } + } else if (child.getParent() == mRecyclerView) { // it was not a scrap but a valid child + // ensure in correct position + int currentIndex = mChildHelper.indexOfChild(child); + if (index == -1) { + index = mChildHelper.getChildCount(); + } + if (currentIndex == -1) { + throw new IllegalStateException("Added View has RecyclerView as parent but" + + " view is not a real child. Unfiltered index:" + + mRecyclerView.indexOfChild(child)); + } + if (currentIndex != index) { + mRecyclerView.mLayout.moveView(currentIndex, index); + } + } else { + mChildHelper.addView(child, index, false); + lp.mInsetsDirty = true; + if (mSmoothScroller != null && mSmoothScroller.isRunning()) { + mSmoothScroller.onChildAttachedToWindow(child); + } + } + if (lp.mPendingInvalidate) { + if (DEBUG) { + Log.d(TAG, "consuming pending invalidate on child " + lp.mViewHolder); + } + holder.itemView.invalidate(); + lp.mPendingInvalidate = false; + } + } + + /** + * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should + * use this method to completely remove a child view that is no longer needed. + * LayoutManagers should strongly consider recycling removed views using + * {@link Recycler#recycleView(android.view.View)}. + * + * @param child View to remove + */ + public void removeView(View child) { + mChildHelper.removeView(child); + } + + /** + * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should + * use this method to completely remove a child view that is no longer needed. + * LayoutManagers should strongly consider recycling removed views using + * {@link Recycler#recycleView(android.view.View)}. + * + * @param index Index of the child view to remove + */ + public void removeViewAt(int index) { + final View child = getChildAt(index); + if (child != null) { + mChildHelper.removeViewAt(index); + } + } + + /** + * Remove all views from the currently attached RecyclerView. This will not recycle + * any of the affected views; the LayoutManager is responsible for doing so if desired. + */ + public void removeAllViews() { + // Only remove non-animating views + final int childCount = getChildCount(); + for (int i = childCount - 1; i >= 0; i--) { + mChildHelper.removeViewAt(i); + } + } + + /** + * Returns offset of the RecyclerView's text baseline from the its top boundary. + * + * @return The offset of the RecyclerView's text baseline from the its top boundary; -1 if + * there is no baseline. + */ + public int getBaseline() { + return -1; + } + + /** + * Returns the adapter position of the item represented by the given View. This does not + * contain any adapter changes that might have happened after the last layout. + * + * @param view The view to query + * @return The adapter position of the item which is rendered by this View. + */ + public int getPosition(View view) { + return ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition(); + } + + /** + * Returns the View type defined by the adapter. + * + * @param view The view to query + * @return The type of the view assigned by the adapter. + */ + public int getItemViewType(View view) { + return getChildViewHolderInt(view).getItemViewType(); + } + + /** + * Finds the view which represents the given adapter position. + *

+ * This method traverses each child since it has no information about child order. + * Override this method to improve performance if your LayoutManager keeps data about + * child views. + *

+ * If a view is ignored via {@link #ignoreView(View)}, it is also ignored by this method. + * + * @param position Position of the item in adapter + * @return The child view that represents the given position or null if the position is not + * laid out + */ + public View findViewByPosition(int position) { + final int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + ViewHolder vh = getChildViewHolderInt(child); + if (vh == null) { + continue; + } + if (vh.getLayoutPosition() == position && !vh.shouldIgnore() && + (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) { + return child; + } + } + return null; + } + + /** + * Temporarily detach a child view. + * + *

LayoutManagers may want to perform a lightweight detach operation to rearrange + * views currently attached to the RecyclerView. Generally LayoutManager implementations + * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)} + * so that the detached view may be rebound and reused.

+ * + *

If a LayoutManager uses this method to detach a view, it must + * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach} + * or {@link #removeDetachedView(android.view.View) fully remove} the detached view + * before the LayoutManager entry point method called by RecyclerView returns.

+ * + * @param child Child to detach + */ + public void detachView(View child) { + final int ind = mChildHelper.indexOfChild(child); + if (ind >= 0) { + detachViewInternal(ind, child); + } + } + + /** + * Temporarily detach a child view. + * + *

LayoutManagers may want to perform a lightweight detach operation to rearrange + * views currently attached to the RecyclerView. Generally LayoutManager implementations + * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)} + * so that the detached view may be rebound and reused.

+ * + *

If a LayoutManager uses this method to detach a view, it must + * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach} + * or {@link #removeDetachedView(android.view.View) fully remove} the detached view + * before the LayoutManager entry point method called by RecyclerView returns.

+ * + * @param index Index of the child to detach + */ + public void detachViewAt(int index) { + detachViewInternal(index, getChildAt(index)); + } + + private void detachViewInternal(int index, View view) { + if (DISPATCH_TEMP_DETACH) { + ViewCompat.dispatchStartTemporaryDetach(view); + } + mChildHelper.detachViewFromParent(index); + } + + /** + * Reattach a previously {@link #detachView(android.view.View) detached} view. + * This method should not be used to reattach views that were previously + * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)} scrapped}. + * + * @param child Child to reattach + * @param index Intended child index for child + * @param lp LayoutParams for child + */ + public void attachView(View child, int index, LayoutParams lp) { + ViewHolder vh = getChildViewHolderInt(child); + if (vh.isRemoved()) { + mRecyclerView.mState.addToDisappearingList(child); + } else { + mRecyclerView.mState.removeFromDisappearingList(child); + } + mChildHelper.attachViewToParent(child, index, lp, vh.isRemoved()); + if (DISPATCH_TEMP_DETACH) { + ViewCompat.dispatchFinishTemporaryDetach(child); + } + } + + /** + * Reattach a previously {@link #detachView(android.view.View) detached} view. + * This method should not be used to reattach views that were previously + * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)} scrapped}. + * + * @param child Child to reattach + * @param index Intended child index for child + */ + public void attachView(View child, int index) { + attachView(child, index, (LayoutParams) child.getLayoutParams()); + } + + /** + * Reattach a previously {@link #detachView(android.view.View) detached} view. + * This method should not be used to reattach views that were previously + * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)} scrapped}. + * + * @param child Child to reattach + */ + public void attachView(View child) { + attachView(child, -1); + } + + /** + * Finish removing a view that was previously temporarily + * {@link #detachView(android.view.View) detached}. + * + * @param child Detached child to remove + */ + public void removeDetachedView(View child) { + mRecyclerView.removeDetachedView(child, false); + } + + /** + * Moves a View from one position to another. + * + * @param fromIndex The View's initial index + * @param toIndex The View's target index + */ + public void moveView(int fromIndex, int toIndex) { + View view = getChildAt(fromIndex); + if (view == null) { + throw new IllegalArgumentException("Cannot move a child from non-existing index:" + + fromIndex); + } + detachViewAt(fromIndex); + attachView(view, toIndex); + } + + /** + * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap. + * + *

Scrapping a view allows it to be rebound and reused to show updated or + * different data.

+ * + * @param child Child to detach and scrap + * @param recycler Recycler to deposit the new scrap view into + */ + public void detachAndScrapView(View child, Recycler recycler) { + int index = mChildHelper.indexOfChild(child); + scrapOrRecycleView(recycler, index, child); + } + + /** + * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap. + * + *

Scrapping a view allows it to be rebound and reused to show updated or + * different data.

+ * + * @param index Index of child to detach and scrap + * @param recycler Recycler to deposit the new scrap view into + */ + public void detachAndScrapViewAt(int index, Recycler recycler) { + final View child = getChildAt(index); + scrapOrRecycleView(recycler, index, child); + } + + /** + * Remove a child view and recycle it using the given Recycler. + * + * @param child Child to remove and recycle + * @param recycler Recycler to use to recycle child + */ + public void removeAndRecycleView(View child, Recycler recycler) { + removeView(child); + recycler.recycleView(child); + } + + /** + * Remove a child view and recycle it using the given Recycler. + * + * @param index Index of child to remove and recycle + * @param recycler Recycler to use to recycle child + */ + public void removeAndRecycleViewAt(int index, Recycler recycler) { + final View view = getChildAt(index); + removeViewAt(index); + recycler.recycleView(view); + } + + /** + * Return the current number of child views attached to the parent RecyclerView. + * This does not include child views that were temporarily detached and/or scrapped. + * + * @return Number of attached children + */ + public int getChildCount() { + return mChildHelper != null ? mChildHelper.getChildCount() : 0; + } + + /** + * Return the child view at the given index + * @param index Index of child to return + * @return Child view at index + */ + public View getChildAt(int index) { + return mChildHelper != null ? mChildHelper.getChildAt(index) : null; + } + + /** + * Return the width of the parent RecyclerView + * + * @return Width in pixels + */ + public int getWidth() { + return mRecyclerView != null ? mRecyclerView.getWidth() : 0; + } + + /** + * Return the height of the parent RecyclerView + * + * @return Height in pixels + */ + public int getHeight() { + return mRecyclerView != null ? mRecyclerView.getHeight() : 0; + } + + /** + * Return the left padding of the parent RecyclerView + * + * @return Padding in pixels + */ + public int getPaddingLeft() { + return mRecyclerView != null ? mRecyclerView.getPaddingLeft() : 0; + } + + /** + * Return the top padding of the parent RecyclerView + * + * @return Padding in pixels + */ + public int getPaddingTop() { + return mRecyclerView != null ? mRecyclerView.getPaddingTop() : 0; + } + + /** + * Return the right padding of the parent RecyclerView + * + * @return Padding in pixels + */ + public int getPaddingRight() { + return mRecyclerView != null ? mRecyclerView.getPaddingRight() : 0; + } + + /** + * Return the bottom padding of the parent RecyclerView + * + * @return Padding in pixels + */ + public int getPaddingBottom() { + return mRecyclerView != null ? mRecyclerView.getPaddingBottom() : 0; + } + + /** + * Return the start padding of the parent RecyclerView + * + * @return Padding in pixels + */ + public int getPaddingStart() { + return mRecyclerView != null ? ViewCompat.getPaddingStart(mRecyclerView) : 0; + } + + /** + * Return the end padding of the parent RecyclerView + * + * @return Padding in pixels + */ + public int getPaddingEnd() { + return mRecyclerView != null ? ViewCompat.getPaddingEnd(mRecyclerView) : 0; + } + + /** + * Returns true if the RecyclerView this LayoutManager is bound to has focus. + * + * @return True if the RecyclerView has focus, false otherwise. + * @see View#isFocused() + */ + public boolean isFocused() { + return mRecyclerView != null && mRecyclerView.isFocused(); + } + + /** + * Returns true if the RecyclerView this LayoutManager is bound to has or contains focus. + * + * @return true if the RecyclerView has or contains focus + * @see View#hasFocus() + */ + public boolean hasFocus() { + return mRecyclerView != null && mRecyclerView.hasFocus(); + } + + /** + * Returns the item View which has or contains focus. + * + * @return A direct child of RecyclerView which has focus or contains the focused child. + */ + public View getFocusedChild() { + if (mRecyclerView == null) { + return null; + } + final View focused = mRecyclerView.getFocusedChild(); + if (focused == null || mChildHelper.isHidden(focused)) { + return null; + } + return focused; + } + + /** + * Returns the number of items in the adapter bound to the parent RecyclerView. + *

+ * Note that this number is not necessarily equal to {@link State#getItemCount()}. In + * methods where State is available, you should use {@link State#getItemCount()} instead. + * For more details, check the documentation for {@link State#getItemCount()}. + * + * @return The number of items in the bound adapter + * @see State#getItemCount() + */ + public int getItemCount() { + final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null; + return a != null ? a.getItemCount() : 0; + } + + /** + * Offset all child views attached to the parent RecyclerView by dx pixels along + * the horizontal axis. + * + * @param dx Pixels to offset by + */ + public void offsetChildrenHorizontal(int dx) { + if (mRecyclerView != null) { + mRecyclerView.offsetChildrenHorizontal(dx); + } + } + + /** + * Offset all child views attached to the parent RecyclerView by dy pixels along + * the vertical axis. + * + * @param dy Pixels to offset by + */ + public void offsetChildrenVertical(int dy) { + if (mRecyclerView != null) { + mRecyclerView.offsetChildrenVertical(dy); + } + } + + /** + * Flags a view so that it will not be scrapped or recycled. + *

+ * Scope of ignoring a child is strictly restricted to position tracking, scrapping and + * recyling. Methods like {@link #removeAndRecycleAllViews(Recycler)} will ignore the child + * whereas {@link #removeAllViews()} or {@link #offsetChildrenHorizontal(int)} will not + * ignore the child. + *

+ * Before this child can be recycled again, you have to call + * {@link #stopIgnoringView(View)}. + *

+ * You can call this method only if your LayoutManger is in onLayout or onScroll callback. + * + * @param view View to ignore. + * @see #stopIgnoringView(View) + */ + public void ignoreView(View view) { + if (view.getParent() != mRecyclerView || mRecyclerView.indexOfChild(view) == -1) { + // checking this because calling this method on a recycled or detached view may + // cause loss of state. + throw new IllegalArgumentException("View should be fully attached to be ignored"); + } + final ViewHolder vh = getChildViewHolderInt(view); + vh.addFlags(ViewHolder.FLAG_IGNORE); + mRecyclerView.mState.onViewIgnored(vh); + } + + /** + * View can be scrapped and recycled again. + *

+ * Note that calling this method removes all information in the view holder. + *

+ * You can call this method only if your LayoutManger is in onLayout or onScroll callback. + * + * @param view View to ignore. + */ + public void stopIgnoringView(View view) { + final ViewHolder vh = getChildViewHolderInt(view); + vh.stopIgnoring(); + vh.resetInternal(); + vh.addFlags(ViewHolder.FLAG_INVALID); + } + + /** + * Temporarily detach and scrap all currently attached child views. Views will be scrapped + * into the given Recycler. The Recycler may prefer to reuse scrap views before + * other views that were previously recycled. + * + * @param recycler Recycler to scrap views into + */ + public void detachAndScrapAttachedViews(Recycler recycler) { + final int childCount = getChildCount(); + for (int i = childCount - 1; i >= 0; i--) { + final View v = getChildAt(i); + scrapOrRecycleView(recycler, i, v); + } + } + + private void scrapOrRecycleView(Recycler recycler, int index, View view) { + final ViewHolder viewHolder = getChildViewHolderInt(view); + if (viewHolder.shouldIgnore()) { + if (DEBUG) { + Log.d(TAG, "ignoring view " + viewHolder); + } + return; + } + if (viewHolder.isInvalid() && !viewHolder.isRemoved() && !viewHolder.isChanged() && + !mRecyclerView.mAdapter.hasStableIds()) { + removeViewAt(index); + recycler.recycleViewHolderInternal(viewHolder); + } else { + detachViewAt(index); + recycler.scrapView(view); + } + } + + /** + * Recycles the scrapped views. + *

+ * When a view is detached and removed, it does not trigger a ViewGroup invalidate. This is + * the expected behavior if scrapped views are used for animations. Otherwise, we need to + * call remove and invalidate RecyclerView to ensure UI update. + * + * @param recycler Recycler + */ + void removeAndRecycleScrapInt(Recycler recycler) { + final int scrapCount = recycler.getScrapCount(); + // Loop backward, recycler might be changed by removeDetachedView() + for (int i = scrapCount - 1; i >= 0; i--) { + final View scrap = recycler.getScrapViewAt(i); + final ViewHolder vh = getChildViewHolderInt(scrap); + if (vh.shouldIgnore()) { + continue; + } + if (vh.isTmpDetached()) { + mRecyclerView.removeDetachedView(scrap, false); + } + recycler.quickRecycleScrapView(scrap); + } + recycler.clearScrap(); + if (scrapCount > 0) { + mRecyclerView.invalidate(); + } + } + + + /** + * Measure a child view using standard measurement policy, taking the padding + * of the parent RecyclerView and any added item decorations into account. + * + *

If the RecyclerView can be scrolled in either dimension the caller may + * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.

+ * + * @param child Child view to measure + * @param widthUsed Width in pixels currently consumed by other views, if relevant + * @param heightUsed Height in pixels currently consumed by other views, if relevant + */ + public void measureChild(View child, int widthUsed, int heightUsed) { + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + + final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child); + widthUsed += insets.left + insets.right; + heightUsed += insets.top + insets.bottom; + + final int widthSpec = getChildMeasureSpec(getWidth(), + getPaddingLeft() + getPaddingRight() + widthUsed, lp.width, + canScrollHorizontally()); + final int heightSpec = getChildMeasureSpec(getHeight(), + getPaddingTop() + getPaddingBottom() + heightUsed, lp.height, + canScrollVertically()); + child.measure(widthSpec, heightSpec); + } + + /** + * Measure a child view using standard measurement policy, taking the padding + * of the parent RecyclerView, any added item decorations and the child margins + * into account. + * + *

If the RecyclerView can be scrolled in either dimension the caller may + * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.

+ * + * @param child Child view to measure + * @param widthUsed Width in pixels currently consumed by other views, if relevant + * @param heightUsed Height in pixels currently consumed by other views, if relevant + */ + public void measureChildWithMargins(View child, int widthUsed, int heightUsed) { + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + + final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child); + widthUsed += insets.left + insets.right; + heightUsed += insets.top + insets.bottom; + + final int widthSpec = getChildMeasureSpec(getWidth(), + getPaddingLeft() + getPaddingRight() + + lp.leftMargin + lp.rightMargin + widthUsed, lp.width, + canScrollHorizontally()); + final int heightSpec = getChildMeasureSpec(getHeight(), + getPaddingTop() + getPaddingBottom() + + lp.topMargin + lp.bottomMargin + heightUsed, lp.height, + canScrollVertically()); + child.measure(widthSpec, heightSpec); + } + + /** + * Calculate a MeasureSpec value for measuring a child view in one dimension. + * + * @param parentSize Size of the parent view where the child will be placed + * @param padding Total space currently consumed by other elements of parent + * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT. + * Generally obtained from the child view's LayoutParams + * @param canScroll true if the parent RecyclerView can scroll in this dimension + * + * @return a MeasureSpec value for the child view + */ + public static int getChildMeasureSpec(int parentSize, int padding, int childDimension, + boolean canScroll) { + int size = Math.max(0, parentSize - padding); + int resultSize = 0; + int resultMode = 0; + + if (canScroll) { + if (childDimension >= 0) { + resultSize = childDimension; + resultMode = MeasureSpec.EXACTLY; + } else { + // MATCH_PARENT can't be applied since we can scroll in this dimension, wrap + // instead using UNSPECIFIED. + resultSize = 0; + resultMode = MeasureSpec.UNSPECIFIED; + } + } else { + if (childDimension >= 0) { + resultSize = childDimension; + resultMode = MeasureSpec.EXACTLY; + } else if (childDimension == LayoutParams.FILL_PARENT) { + resultSize = size; + resultMode = MeasureSpec.EXACTLY; + } else if (childDimension == LayoutParams.WRAP_CONTENT) { + resultSize = size; + resultMode = MeasureSpec.AT_MOST; + } + } + return MeasureSpec.makeMeasureSpec(resultSize, resultMode); + } + + /** + * Returns the measured width of the given child, plus the additional size of + * any insets applied by {@link ItemDecoration ItemDecorations}. + * + * @param child Child view to query + * @return child's measured width plus ItemDecoration insets + * + * @see View#getMeasuredWidth() + */ + public int getDecoratedMeasuredWidth(View child) { + final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets; + return child.getMeasuredWidth() + insets.left + insets.right; + } + + /** + * Returns the measured height of the given child, plus the additional size of + * any insets applied by {@link ItemDecoration ItemDecorations}. + * + * @param child Child view to query + * @return child's measured height plus ItemDecoration insets + * + * @see View#getMeasuredHeight() + */ + public int getDecoratedMeasuredHeight(View child) { + final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets; + return child.getMeasuredHeight() + insets.top + insets.bottom; + } + + /** + * Lay out the given child view within the RecyclerView using coordinates that + * include any current {@link ItemDecoration ItemDecorations}. + * + *

LayoutManagers should prefer working in sizes and coordinates that include + * item decoration insets whenever possible. This allows the LayoutManager to effectively + * ignore decoration insets within measurement and layout code. See the following + * methods:

+ *
    + *
  • {@link #measureChild(View, int, int)}
  • + *
  • {@link #measureChildWithMargins(View, int, int)}
  • + *
  • {@link #getDecoratedLeft(View)}
  • + *
  • {@link #getDecoratedTop(View)}
  • + *
  • {@link #getDecoratedRight(View)}
  • + *
  • {@link #getDecoratedBottom(View)}
  • + *
  • {@link #getDecoratedMeasuredWidth(View)}
  • + *
  • {@link #getDecoratedMeasuredHeight(View)}
  • + *
+ * + * @param child Child to lay out + * @param left Left edge, with item decoration insets included + * @param top Top edge, with item decoration insets included + * @param right Right edge, with item decoration insets included + * @param bottom Bottom edge, with item decoration insets included + * + * @see View#layout(int, int, int, int) + */ + public void layoutDecorated(View child, int left, int top, int right, int bottom) { + final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets; + child.layout(left + insets.left, top + insets.top, right - insets.right, + bottom - insets.bottom); + } + + /** + * Returns the left edge of the given child view within its parent, offset by any applied + * {@link ItemDecoration ItemDecorations}. + * + * @param child Child to query + * @return Child left edge with offsets applied + * @see #getLeftDecorationWidth(View) + */ + public int getDecoratedLeft(View child) { + return child.getLeft() - getLeftDecorationWidth(child); + } + + /** + * Returns the top edge of the given child view within its parent, offset by any applied + * {@link ItemDecoration ItemDecorations}. + * + * @param child Child to query + * @return Child top edge with offsets applied + * @see #getTopDecorationHeight(View) + */ + public int getDecoratedTop(View child) { + return child.getTop() - getTopDecorationHeight(child); + } + + /** + * Returns the right edge of the given child view within its parent, offset by any applied + * {@link ItemDecoration ItemDecorations}. + * + * @param child Child to query + * @return Child right edge with offsets applied + * @see #getRightDecorationWidth(View) + */ + public int getDecoratedRight(View child) { + return child.getRight() + getRightDecorationWidth(child); + } + + /** + * Returns the bottom edge of the given child view within its parent, offset by any applied + * {@link ItemDecoration ItemDecorations}. + * + * @param child Child to query + * @return Child bottom edge with offsets applied + * @see #getBottomDecorationHeight(View) + */ + public int getDecoratedBottom(View child) { + return child.getBottom() + getBottomDecorationHeight(child); + } + + /** + * Calculates the item decor insets applied to the given child and updates the provided + * Rect instance with the inset values. + *
    + *
  • The Rect's left is set to the total width of left decorations.
  • + *
  • The Rect's top is set to the total height of top decorations.
  • + *
  • The Rect's right is set to the total width of right decorations.
  • + *
  • The Rect's bottom is set to total height of bottom decorations.
  • + *
+ *

+ * Note that item decorations are automatically calculated when one of the LayoutManager's + * measure child methods is called. If you need to measure the child with custom specs via + * {@link View#measure(int, int)}, you can use this method to get decorations. + * + * @param child The child view whose decorations should be calculated + * @param outRect The Rect to hold result values + */ + public void calculateItemDecorationsForChild(View child, Rect outRect) { + if (mRecyclerView == null) { + outRect.set(0, 0, 0, 0); + return; + } + Rect insets = mRecyclerView.getItemDecorInsetsForChild(child); + outRect.set(insets); + } + + /** + * Returns the total height of item decorations applied to child's top. + *

+ * Note that this value is not updated until the View is measured or + * {@link #calculateItemDecorationsForChild(View, Rect)} is called. + * + * @param child Child to query + * @return The total height of item decorations applied to the child's top. + * @see #getDecoratedTop(View) + * @see #calculateItemDecorationsForChild(View, Rect) + */ + public int getTopDecorationHeight(View child) { + return ((LayoutParams) child.getLayoutParams()).mDecorInsets.top; + } + + /** + * Returns the total height of item decorations applied to child's bottom. + *

+ * Note that this value is not updated until the View is measured or + * {@link #calculateItemDecorationsForChild(View, Rect)} is called. + * + * @param child Child to query + * @return The total height of item decorations applied to the child's bottom. + * @see #getDecoratedBottom(View) + * @see #calculateItemDecorationsForChild(View, Rect) + */ + public int getBottomDecorationHeight(View child) { + return ((LayoutParams) child.getLayoutParams()).mDecorInsets.bottom; + } + + /** + * Returns the total width of item decorations applied to child's left. + *

+ * Note that this value is not updated until the View is measured or + * {@link #calculateItemDecorationsForChild(View, Rect)} is called. + * + * @param child Child to query + * @return The total width of item decorations applied to the child's left. + * @see #getDecoratedLeft(View) + * @see #calculateItemDecorationsForChild(View, Rect) + */ + public int getLeftDecorationWidth(View child) { + return ((LayoutParams) child.getLayoutParams()).mDecorInsets.left; + } + + /** + * Returns the total width of item decorations applied to child's right. + *

+ * Note that this value is not updated until the View is measured or + * {@link #calculateItemDecorationsForChild(View, Rect)} is called. + * + * @param child Child to query + * @return The total width of item decorations applied to the child's right. + * @see #getDecoratedRight(View) + * @see #calculateItemDecorationsForChild(View, Rect) + */ + public int getRightDecorationWidth(View child) { + return ((LayoutParams) child.getLayoutParams()).mDecorInsets.right; + } + + /** + * Called when searching for a focusable view in the given direction has failed + * for the current content of the RecyclerView. + * + *

This is the LayoutManager's opportunity to populate views in the given direction + * to fulfill the request if it can. The LayoutManager should attach and return + * the view to be focused. The default implementation returns null.

+ * + * @param focused The currently focused view + * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN}, + * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, + * {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD} + * or 0 for not applicable + * @param recycler The recycler to use for obtaining views for currently offscreen items + * @param state Transient state of RecyclerView + * @return The chosen view to be focused + */ + @Nullable + public View onFocusSearchFailed(View focused, int direction, Recycler recycler, + State state) { + return null; + } + + /** + * This method gives a LayoutManager an opportunity to intercept the initial focus search + * before the default behavior of {@link FocusFinder} is used. If this method returns + * null FocusFinder will attempt to find a focusable child view. If it fails + * then {@link #onFocusSearchFailed(View, int, RecyclerView.Recycler, RecyclerView.State)} + * will be called to give the LayoutManager an opportunity to add new views for items + * that did not have attached views representing them. The LayoutManager should not add + * or remove views from this method. + * + * @param focused The currently focused view + * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN}, + * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, + * {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD} + * @return A descendant view to focus or null to fall back to default behavior. + * The default implementation returns null. + */ + public View onInterceptFocusSearch(View focused, int direction) { + return null; + } + + /** + * Called when a child of the RecyclerView wants a particular rectangle to be positioned + * onto the screen. See {@link ViewParent#requestChildRectangleOnScreen(android.view.View, + * android.graphics.Rect, boolean)} for more details. + * + *

The base implementation will attempt to perform a standard programmatic scroll + * to bring the given rect into view, within the padded area of the RecyclerView.

+ * + * @param child The direct child making the request. + * @param rect The rectangle in the child's coordinates the child + * wishes to be on the screen. + * @param immediate True to forbid animated or delayed scrolling, + * false otherwise + * @return Whether the group scrolled to handle the operation + */ + public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect, + boolean immediate) { + final int parentLeft = getPaddingLeft(); + final int parentTop = getPaddingTop(); + final int parentRight = getWidth() - getPaddingRight(); + final int parentBottom = getHeight() - getPaddingBottom(); + final int childLeft = child.getLeft() + rect.left; + final int childTop = child.getTop() + rect.top; + final int childRight = childLeft + rect.width(); + final int childBottom = childTop + rect.height(); + + final int offScreenLeft = Math.min(0, childLeft - parentLeft); + final int offScreenTop = Math.min(0, childTop - parentTop); + final int offScreenRight = Math.max(0, childRight - parentRight); + final int offScreenBottom = Math.max(0, childBottom - parentBottom); + + // Favor the "start" layout direction over the end when bringing one side or the other + // of a large rect into view. If we decide to bring in end because start is already + // visible, limit the scroll such that start won't go out of bounds. + final int dx; + if (getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL) { + dx = offScreenRight != 0 ? offScreenRight + : Math.max(offScreenLeft, childRight - parentRight); + } else { + dx = offScreenLeft != 0 ? offScreenLeft + : Math.min(childLeft - parentLeft, offScreenRight); + } + + // Favor bringing the top into view over the bottom. If top is already visible and + // we should scroll to make bottom visible, make sure top does not go out of bounds. + final int dy = offScreenTop != 0 ? offScreenTop + : Math.min(childTop - parentTop, offScreenBottom); + + if (dx != 0 || dy != 0) { + if (immediate) { + parent.scrollBy(dx, dy); + } else { + parent.smoothScrollBy(dx, dy); + } + return true; + } + return false; + } + + /** + * @deprecated Use {@link #onRequestChildFocus(RecyclerView, State, View, View)} + */ + @Deprecated + public boolean onRequestChildFocus(RecyclerView parent, View child, View focused) { + // eat the request if we are in the middle of a scroll or layout + return isSmoothScrolling() || parent.isRunningLayoutOrScroll(); + } + + /** + * Called when a descendant view of the RecyclerView requests focus. + * + *

A LayoutManager wishing to keep focused views aligned in a specific + * portion of the view may implement that behavior in an override of this method.

+ * + *

If the LayoutManager executes different behavior that should override the default + * behavior of scrolling the focused child on screen instead of running alongside it, + * this method should return true.

+ * + * @param parent The RecyclerView hosting this LayoutManager + * @param state Current state of RecyclerView + * @param child Direct child of the RecyclerView containing the newly focused view + * @param focused The newly focused view. This may be the same view as child or it may be + * null + * @return true if the default scroll behavior should be suppressed + */ + public boolean onRequestChildFocus(RecyclerView parent, State state, View child, + View focused) { + return onRequestChildFocus(parent, child, focused); + } + + /** + * Called if the RecyclerView this LayoutManager is bound to has a different adapter set. + * The LayoutManager may use this opportunity to clear caches and configure state such + * that it can relayout appropriately with the new data and potentially new view types. + * + *

The default implementation removes all currently attached views.

+ * + * @param oldAdapter The previous adapter instance. Will be null if there was previously no + * adapter. + * @param newAdapter The new adapter instance. Might be null if + * {@link #setAdapter(RecyclerView.Adapter)} is called with {@code null}. + */ + public void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter) { + } + + /** + * Called to populate focusable views within the RecyclerView. + * + *

The LayoutManager implementation should return true if the default + * behavior of {@link ViewGroup#addFocusables(java.util.ArrayList, int)} should be + * suppressed.

+ * + *

The default implementation returns false to trigger RecyclerView + * to fall back to the default ViewGroup behavior.

+ * + * @param recyclerView The RecyclerView hosting this LayoutManager + * @param views List of output views. This method should add valid focusable views + * to this list. + * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN}, + * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, + * {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD} + * @param focusableMode The type of focusables to be added. + * + * @return true to suppress the default behavior, false to add default focusables after + * this method returns. + * + * @see #FOCUSABLES_ALL + * @see #FOCUSABLES_TOUCH_MODE + */ + public boolean onAddFocusables(RecyclerView recyclerView, ArrayList views, + int direction, int focusableMode) { + return false; + } + + /** + * Called when {@link Adapter#notifyDataSetChanged()} is triggered instead of giving + * detailed information on what has actually changed. + * + * @param recyclerView + */ + public void onItemsChanged(RecyclerView recyclerView) { + } + + /** + * Called when items have been added to the adapter. The LayoutManager may choose to + * requestLayout if the inserted items would require refreshing the currently visible set + * of child views. (e.g. currently empty space would be filled by appended items, etc.) + * + * @param recyclerView + * @param positionStart + * @param itemCount + */ + public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) { + } + + /** + * Called when items have been removed from the adapter. + * + * @param recyclerView + * @param positionStart + * @param itemCount + */ + public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) { + } + + /** + * Called when items have been changed in the adapter. + * + * @param recyclerView + * @param positionStart + * @param itemCount + */ + public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) { + } + + /** + * Called when an item is moved withing the adapter. + *

+ * Note that, an item may also change position in response to another ADD/REMOVE/MOVE + * operation. This callback is only called if and only if {@link Adapter#notifyItemMoved} + * is called. + * + * @param recyclerView + * @param from + * @param to + * @param itemCount + */ + public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) { + + } + + + /** + *

Override this method if you want to support scroll bars.

+ * + *

Read {@link RecyclerView#computeHorizontalScrollExtent()} for details.

+ * + *

Default implementation returns 0.

+ * + * @param state Current state of RecyclerView + * @return The horizontal extent of the scrollbar's thumb + * @see RecyclerView#computeHorizontalScrollExtent() + */ + public int computeHorizontalScrollExtent(State state) { + return 0; + } + + /** + *

Override this method if you want to support scroll bars.

+ * + *

Read {@link RecyclerView#computeHorizontalScrollOffset()} for details.

+ * + *

Default implementation returns 0.

+ * + * @param state Current State of RecyclerView where you can find total item count + * @return The horizontal offset of the scrollbar's thumb + * @see RecyclerView#computeHorizontalScrollOffset() + */ + public int computeHorizontalScrollOffset(State state) { + return 0; + } + + /** + *

Override this method if you want to support scroll bars.

+ * + *

Read {@link RecyclerView#computeHorizontalScrollRange()} for details.

+ * + *

Default implementation returns 0.

+ * + * @param state Current State of RecyclerView where you can find total item count + * @return The total horizontal range represented by the vertical scrollbar + * @see RecyclerView#computeHorizontalScrollRange() + */ + public int computeHorizontalScrollRange(State state) { + return 0; + } + + /** + *

Override this method if you want to support scroll bars.

+ * + *

Read {@link RecyclerView#computeVerticalScrollExtent()} for details.

+ * + *

Default implementation returns 0.

+ * + * @param state Current state of RecyclerView + * @return The vertical extent of the scrollbar's thumb + * @see RecyclerView#computeVerticalScrollExtent() + */ + public int computeVerticalScrollExtent(State state) { + return 0; + } + + /** + *

Override this method if you want to support scroll bars.

+ * + *

Read {@link RecyclerView#computeVerticalScrollOffset()} for details.

+ * + *

Default implementation returns 0.

+ * + * @param state Current State of RecyclerView where you can find total item count + * @return The vertical offset of the scrollbar's thumb + * @see RecyclerView#computeVerticalScrollOffset() + */ + public int computeVerticalScrollOffset(State state) { + return 0; + } + + /** + *

Override this method if you want to support scroll bars.

+ * + *

Read {@link RecyclerView#computeVerticalScrollRange()} for details.

+ * + *

Default implementation returns 0.

+ * + * @param state Current State of RecyclerView where you can find total item count + * @return The total vertical range represented by the vertical scrollbar + * @see RecyclerView#computeVerticalScrollRange() + */ + public int computeVerticalScrollRange(State state) { + return 0; + } + + /** + * Measure the attached RecyclerView. Implementations must call + * {@link #setMeasuredDimension(int, int)} before returning. + * + *

The default implementation will handle EXACTLY measurements and respect + * the minimum width and height properties of the host RecyclerView if measured + * as UNSPECIFIED. AT_MOST measurements will be treated as EXACTLY and the RecyclerView + * will consume all available space.

+ * + * @param recycler Recycler + * @param state Transient state of RecyclerView + * @param widthSpec Width {@link android.view.View.MeasureSpec} + * @param heightSpec Height {@link android.view.View.MeasureSpec} + */ + public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) { + mRecyclerView.defaultOnMeasure(widthSpec, heightSpec); + } + + /** + * {@link View#setMeasuredDimension(int, int) Set the measured dimensions} of the + * host RecyclerView. + * + * @param widthSize Measured width + * @param heightSize Measured height + */ + public void setMeasuredDimension(int widthSize, int heightSize) { + mRecyclerView.setMeasuredDimension(widthSize, heightSize); + } + + /** + * @return The host RecyclerView's {@link View#getMinimumWidth()} + */ + public int getMinimumWidth() { + return ViewCompat.getMinimumWidth(mRecyclerView); + } + + /** + * @return The host RecyclerView's {@link View#getMinimumHeight()} + */ + public int getMinimumHeight() { + return ViewCompat.getMinimumHeight(mRecyclerView); + } + /** + *

Called when the LayoutManager should save its state. This is a good time to save your + * scroll position, configuration and anything else that may be required to restore the same + * layout state if the LayoutManager is recreated.

+ *

RecyclerView does NOT verify if the LayoutManager has changed between state save and + * restore. This will let you share information between your LayoutManagers but it is also + * your responsibility to make sure they use the same parcelable class.

+ * + * @return Necessary information for LayoutManager to be able to restore its state + */ + public Parcelable onSaveInstanceState() { + return null; + } + + + public void onRestoreInstanceState(Parcelable state) { + + } + + void stopSmoothScroller() { + if (mSmoothScroller != null) { + mSmoothScroller.stop(); + } + } + + private void onSmoothScrollerStopped(SmoothScroller smoothScroller) { + if (mSmoothScroller == smoothScroller) { + mSmoothScroller = null; + } + } + + /** + * RecyclerView calls this method to notify LayoutManager that scroll state has changed. + * + * @param state The new scroll state for RecyclerView + */ + public void onScrollStateChanged(int state) { + } + + /** + * Removes all views and recycles them using the given recycler. + *

+ * If you want to clean cached views as well, you should call {@link Recycler#clear()} too. + *

+ * If a View is marked as "ignored", it is not removed nor recycled. + * + * @param recycler Recycler to use to recycle children + * @see #removeAndRecycleView(View, Recycler) + * @see #removeAndRecycleViewAt(int, Recycler) + * @see #ignoreView(View) + */ + public void removeAndRecycleAllViews(Recycler recycler) { + for (int i = getChildCount() - 1; i >= 0; i--) { + final View view = getChildAt(i); + if (!getChildViewHolderInt(view).shouldIgnore()) { + removeAndRecycleViewAt(i, recycler); + } + } + } + + // called by accessibility delegate + void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info) { + onInitializeAccessibilityNodeInfo(mRecyclerView.mRecycler, mRecyclerView.mState, info); + } + + /** + * Called by the AccessibilityDelegate when the information about the current layout should + * be populated. + *

+ * Default implementation adds a {@link + * android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat}. + *

+ * You should override + * {@link #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)}, + * {@link #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)}, + * {@link #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)} and + * {@link #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)} for + * more accurate accessibility information. + * + * @param recycler The Recycler that can be used to convert view positions into adapter + * positions + * @param state The current state of RecyclerView + * @param info The info that should be filled by the LayoutManager + * @see View#onInitializeAccessibilityNodeInfo( + *android.view.accessibility.AccessibilityNodeInfo) + * @see #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State) + * @see #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State) + * @see #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State) + * @see #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State) + */ + public void onInitializeAccessibilityNodeInfo(Recycler recycler, State state, + AccessibilityNodeInfoCompat info) { + if (ViewCompat.canScrollVertically(mRecyclerView, -1) || + ViewCompat.canScrollHorizontally(mRecyclerView, -1)) { + info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD); + info.setScrollable(true); + } + if (ViewCompat.canScrollVertically(mRecyclerView, 1) || + ViewCompat.canScrollHorizontally(mRecyclerView, 1)) { + info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD); + info.setScrollable(true); + } + final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo + = AccessibilityNodeInfoCompat.CollectionInfoCompat + .obtain(getRowCountForAccessibility(recycler, state), + getColumnCountForAccessibility(recycler, state), + isLayoutHierarchical(recycler, state), + getSelectionModeForAccessibility(recycler, state)); + info.setCollectionInfo(collectionInfo); + } + + // called by accessibility delegate + public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + onInitializeAccessibilityEvent(mRecyclerView.mRecycler, mRecyclerView.mState, event); + } + + /** + * Called by the accessibility delegate to initialize an accessibility event. + *

+ * Default implementation adds item count and scroll information to the event. + * + * @param recycler The Recycler that can be used to convert view positions into adapter + * positions + * @param state The current state of RecyclerView + * @param event The event instance to initialize + * @see View#onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent) + */ + public void onInitializeAccessibilityEvent(Recycler recycler, State state, + AccessibilityEvent event) { + final AccessibilityRecordCompat record = AccessibilityEventCompat + .asRecord(event); + if (mRecyclerView == null || record == null) { + return; + } + record.setScrollable(ViewCompat.canScrollVertically(mRecyclerView, 1) + || ViewCompat.canScrollVertically(mRecyclerView, -1) + || ViewCompat.canScrollHorizontally(mRecyclerView, -1) + || ViewCompat.canScrollHorizontally(mRecyclerView, 1)); + + if (mRecyclerView.mAdapter != null) { + record.setItemCount(mRecyclerView.mAdapter.getItemCount()); + } + } + + // called by accessibility delegate + void onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfoCompat info) { + final ViewHolder vh = getChildViewHolderInt(host); + // avoid trying to create accessibility node info for removed children + if (vh != null && !vh.isRemoved()) { + onInitializeAccessibilityNodeInfoForItem(mRecyclerView.mRecycler, + mRecyclerView.mState, host, info); + } + } + + /** + * Called by the AccessibilityDelegate when the accessibility information for a specific + * item should be populated. + *

+ * Default implementation adds basic positioning information about the item. + * + * @param recycler The Recycler that can be used to convert view positions into adapter + * positions + * @param state The current state of RecyclerView + * @param host The child for which accessibility node info should be populated + * @param info The info to fill out about the item + * @see android.widget.AbsListView#onInitializeAccessibilityNodeInfoForItem(View, int, + * android.view.accessibility.AccessibilityNodeInfo) + */ + public void onInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state, + View host, AccessibilityNodeInfoCompat info) { + int rowIndexGuess = canScrollVertically() ? getPosition(host) : 0; + int columnIndexGuess = canScrollHorizontally() ? getPosition(host) : 0; + final AccessibilityNodeInfoCompat.CollectionItemInfoCompat itemInfo + = AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(rowIndexGuess, 1, + columnIndexGuess, 1, false, false); + info.setCollectionItemInfo(itemInfo); + } + + /** + * A LayoutManager can call this method to force RecyclerView to run simple animations in + * the next layout pass, even if there is not any trigger to do so. (e.g. adapter data + * change). + *

+ * Note that, calling this method will not guarantee that RecyclerView will run animations + * at all. For example, if there is not any {@link ItemAnimator} set, RecyclerView will + * not run any animations but will still clear this flag after the layout is complete. + * + */ + public void requestSimpleAnimationsInNextLayout() { + mRequestedSimpleAnimations = true; + } + + /** + * Returns the selection mode for accessibility. Should be + * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}, + * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_SINGLE} or + * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_MULTIPLE}. + *

+ * Default implementation returns + * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}. + * + * @param recycler The Recycler that can be used to convert view positions into adapter + * positions + * @param state The current state of RecyclerView + * @return Selection mode for accessibility. Default implementation returns + * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}. + */ + public int getSelectionModeForAccessibility(Recycler recycler, State state) { + return AccessibilityNodeInfoCompat.CollectionInfoCompat.SELECTION_MODE_NONE; + } + + /** + * Returns the number of rows for accessibility. + *

+ * Default implementation returns the number of items in the adapter if LayoutManager + * supports vertical scrolling or 1 if LayoutManager does not support vertical + * scrolling. + * + * @param recycler The Recycler that can be used to convert view positions into adapter + * positions + * @param state The current state of RecyclerView + * @return The number of rows in LayoutManager for accessibility. + */ + public int getRowCountForAccessibility(Recycler recycler, State state) { + if (mRecyclerView == null || mRecyclerView.mAdapter == null) { + return 1; + } + return canScrollVertically() ? mRecyclerView.mAdapter.getItemCount() : 1; + } + + /** + * Returns the number of columns for accessibility. + *

+ * Default implementation returns the number of items in the adapter if LayoutManager + * supports horizontal scrolling or 1 if LayoutManager does not support horizontal + * scrolling. + * + * @param recycler The Recycler that can be used to convert view positions into adapter + * positions + * @param state The current state of RecyclerView + * @return The number of rows in LayoutManager for accessibility. + */ + public int getColumnCountForAccessibility(Recycler recycler, State state) { + if (mRecyclerView == null || mRecyclerView.mAdapter == null) { + return 1; + } + return canScrollHorizontally() ? mRecyclerView.mAdapter.getItemCount() : 1; + } + + /** + * Returns whether layout is hierarchical or not to be used for accessibility. + *

+ * Default implementation returns false. + * + * @param recycler The Recycler that can be used to convert view positions into adapter + * positions + * @param state The current state of RecyclerView + * @return True if layout is hierarchical. + */ + public boolean isLayoutHierarchical(Recycler recycler, State state) { + return false; + } + + // called by accessibility delegate + boolean performAccessibilityAction(int action, Bundle args) { + return performAccessibilityAction(mRecyclerView.mRecycler, mRecyclerView.mState, + action, args); + } + + /** + * Called by AccessibilityDelegate when an action is requested from the RecyclerView. + * + * @param recycler The Recycler that can be used to convert view positions into adapter + * positions + * @param state The current state of RecyclerView + * @param action The action to perform + * @param args Optional action arguments + * @see View#performAccessibilityAction(int, android.os.Bundle) + */ + public boolean performAccessibilityAction(Recycler recycler, State state, int action, + Bundle args) { + if (mRecyclerView == null) { + return false; + } + int vScroll = 0, hScroll = 0; + switch (action) { + case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: + if (ViewCompat.canScrollVertically(mRecyclerView, -1)) { + vScroll = -(getHeight() - getPaddingTop() - getPaddingBottom()); + } + if (ViewCompat.canScrollHorizontally(mRecyclerView, -1)) { + hScroll = -(getWidth() - getPaddingLeft() - getPaddingRight()); + } + break; + case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: + if (ViewCompat.canScrollVertically(mRecyclerView, 1)) { + vScroll = getHeight() - getPaddingTop() - getPaddingBottom(); + } + if (ViewCompat.canScrollHorizontally(mRecyclerView, 1)) { + hScroll = getWidth() - getPaddingLeft() - getPaddingRight(); + } + break; + } + if (vScroll == 0 && hScroll == 0) { + return false; + } + mRecyclerView.scrollBy(hScroll, vScroll); + return true; + } + + // called by accessibility delegate + boolean performAccessibilityActionForItem(View view, int action, Bundle args) { + return performAccessibilityActionForItem(mRecyclerView.mRecycler, mRecyclerView.mState, + view, action, args); + } + + /** + * Called by AccessibilityDelegate when an accessibility action is requested on one of the + * children of LayoutManager. + *

+ * Default implementation does not do anything. + * + * @param recycler The Recycler that can be used to convert view positions into adapter + * positions + * @param state The current state of RecyclerView + * @param view The child view on which the action is performed + * @param action The action to perform + * @param args Optional action arguments + * @return true if action is handled + * @see View#performAccessibilityAction(int, android.os.Bundle) + */ + public boolean performAccessibilityActionForItem(Recycler recycler, State state, View view, + int action, Bundle args) { + return false; + } + } + + /** + * An ItemDecoration allows the application to add a special drawing and layout offset + * to specific item views from the adapter's data set. This can be useful for drawing dividers + * between items, highlights, visual grouping boundaries and more. + * + *

All ItemDecorations are drawn in the order they were added, before the item + * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()} + * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView, + * RecyclerView.State)}.

+ */ + public static abstract class ItemDecoration { + /** + * Draw any appropriate decorations into the Canvas supplied to the RecyclerView. + * Any content drawn by this method will be drawn before the item views are drawn, + * and will thus appear underneath the views. + * + * @param c Canvas to draw into + * @param parent RecyclerView this ItemDecoration is drawing into + * @param state The current state of RecyclerView + */ + public void onDraw(Canvas c, RecyclerView parent, State state) { + onDraw(c, parent); + } + + /** + * @deprecated + * Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)} + */ + @Deprecated + public void onDraw(Canvas c, RecyclerView parent) { + } + + /** + * Draw any appropriate decorations into the Canvas supplied to the RecyclerView. + * Any content drawn by this method will be drawn after the item views are drawn + * and will thus appear over the views. + * + * @param c Canvas to draw into + * @param parent RecyclerView this ItemDecoration is drawing into + * @param state The current state of RecyclerView. + */ + public void onDrawOver(Canvas c, RecyclerView parent, State state) { + onDrawOver(c, parent); + } + + /** + * @deprecated + * Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)} + */ + @Deprecated + public void onDrawOver(Canvas c, RecyclerView parent) { + } + + + /** + * @deprecated + * Use {@link #getItemOffsets(Rect, View, RecyclerView, State)} + */ + @Deprecated + public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { + outRect.set(0, 0, 0, 0); + } + + /** + * Retrieve any offsets for the given item. Each field of outRect specifies + * the number of pixels that the item view should be inset by, similar to padding or margin. + * The default implementation sets the bounds of outRect to 0 and returns. + * + *

+ * If this ItemDecoration does not affect the positioning of item views, it should set + * all four fields of outRect (left, top, right, bottom) to zero + * before returning. + * + *

+ * If you need to access Adapter for additional data, you can call + * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the + * View. + * + * @param outRect Rect to receive the output. + * @param view The child view to decorate + * @param parent RecyclerView this ItemDecoration is decorating + * @param state The current state of RecyclerView. + */ + public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) { + getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(), + parent); + } + } + + /** + * An OnItemTouchListener allows the application to intercept touch events in progress at the + * view hierarchy level of the RecyclerView before those touch events are considered for + * RecyclerView's own scrolling behavior. + * + *

This can be useful for applications that wish to implement various forms of gestural + * manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept + * a touch interaction already in progress even if the RecyclerView is already handling that + * gesture stream itself for the purposes of scrolling.

+ */ + public interface OnItemTouchListener { + /** + * Silently observe and/or take over touch events sent to the RecyclerView + * before they are handled by either the RecyclerView itself or its child views. + * + *

The onInterceptTouchEvent methods of each attached OnItemTouchListener will be run + * in the order in which each listener was added, before any other touch processing + * by the RecyclerView itself or child views occurs.

+ * + * @param e MotionEvent describing the touch event. All coordinates are in + * the RecyclerView's coordinate system. + * @return true if this OnItemTouchListener wishes to begin intercepting touch events, false + * to continue with the current behavior and continue observing future events in + * the gesture. + */ + public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e); + + /** + * Process a touch event as part of a gesture that was claimed by returning true from + * a previous call to {@link #onInterceptTouchEvent}. + * + * @param e MotionEvent describing the touch event. All coordinates are in + * the RecyclerView's coordinate system. + */ + public void onTouchEvent(RecyclerView rv, MotionEvent e); + } + + /** + * An OnScrollListener can be set on a RecyclerView to receive messages + * when a scrolling event has occurred on that RecyclerView. + * + * @see RecyclerView#setOnScrollListener(OnScrollListener) and + * RecyclerView#addOnScrollListener(OnScrollListener) + * + * If you are planning to have several listeners at the same time, use + * RecyclerView#addOnScrollListener. If there will be only one listener at the time and you + * want your components to be able to easily replace the listener use + * RecyclerView#setOnScrollListener. + */ + abstract static public class OnScrollListener { + /** + * Callback method to be invoked when RecyclerView's scroll state changes. + * + * @param recyclerView The RecyclerView whose scroll state has changed. + * @param newState The updated scroll state. One of {@link #SCROLL_STATE_IDLE}, + * {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}. + */ + public void onScrollStateChanged(RecyclerView recyclerView, int newState){} + + /** + * Callback method to be invoked when the RecyclerView has been scrolled. This will be + * called after the scroll has completed. + *

+ * This callback will also be called if visible item range changes after a layout + * calculation. In that case, dx and dy will be 0. + * + * @param recyclerView The RecyclerView which scrolled. + * @param dx The amount of horizontal scroll. + * @param dy The amount of vertical scroll. + */ + public void onScrolled(RecyclerView recyclerView, int dx, int dy){} + } + + /** + * A RecyclerListener can be set on a RecyclerView to receive messages whenever + * a view is recycled. + * + * @see RecyclerView#setRecyclerListener(RecyclerListener) + */ + public interface RecyclerListener { + + /** + * This method is called whenever the view in the ViewHolder is recycled. + * + * RecyclerView calls this method right before clearing ViewHolder's internal data and + * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information + * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get + * its adapter position. + * + * @param holder The ViewHolder containing the view that was recycled + */ + public void onViewRecycled(ViewHolder holder); + } + + /** + * A ViewHolder describes an item view and metadata about its place within the RecyclerView. + * + *

{@link Adapter} implementations should subclass ViewHolder and add fields for caching + * potentially expensive {@link View#findViewById(int)} results.

+ * + *

While {@link LayoutParams} belong to the {@link LayoutManager}, + * {@link ViewHolder ViewHolders} belong to the adapter. Adapters should feel free to use + * their own custom ViewHolder implementations to store data that makes binding view contents + * easier. Implementations should assume that individual item views will hold strong references + * to ViewHolder objects and that RecyclerView instances may hold + * strong references to extra off-screen item views for caching purposes

+ */ + public static abstract class ViewHolder { + public final View itemView; + int mPosition = NO_POSITION; + int mOldPosition = NO_POSITION; + long mItemId = NO_ID; + int mItemViewType = INVALID_TYPE; + int mPreLayoutPosition = NO_POSITION; + + // The item that this holder is shadowing during an item change event/animation + ViewHolder mShadowedHolder = null; + // The item that is shadowing this holder during an item change event/animation + ViewHolder mShadowingHolder = null; + + /** + * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType + * are all valid. + */ + static final int FLAG_BOUND = 1 << 0; + + /** + * The data this ViewHolder's view reflects is stale and needs to be rebound + * by the adapter. mPosition and mItemId are consistent. + */ + static final int FLAG_UPDATE = 1 << 1; + + /** + * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId + * are not to be trusted and may no longer match the item view type. + * This ViewHolder must be fully rebound to different data. + */ + static final int FLAG_INVALID = 1 << 2; + + /** + * This ViewHolder points at data that represents an item previously removed from the + * data set. Its view may still be used for things like outgoing animations. + */ + static final int FLAG_REMOVED = 1 << 3; + + /** + * This ViewHolder should not be recycled. This flag is set via setIsRecyclable() + * and is intended to keep views around during animations. + */ + static final int FLAG_NOT_RECYCLABLE = 1 << 4; + + /** + * This ViewHolder is returned from scrap which means we are expecting an addView call + * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until + * the end of the layout pass and then recycled by RecyclerView if it is not added back to + * the RecyclerView. + */ + static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5; + + /** + * This ViewHolder's contents have changed. This flag is used as an indication that + * change animations may be used, if supported by the ItemAnimator. + */ + static final int FLAG_CHANGED = 1 << 6; + + /** + * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove + * it unless LayoutManager is replaced. + * It is still fully visible to the LayoutManager. + */ + static final int FLAG_IGNORE = 1 << 7; + + /** + * When the View is detached form the parent, we set this flag so that we can take correct + * action when we need to remove it or add it back. + */ + static final int FLAG_TMP_DETACHED = 1 << 8; + + /** + * Set when we can no longer determine the adapter position of this ViewHolder until it is + * rebound to a new position. It is different than FLAG_INVALID because FLAG_INVALID is + * set even when the type does not match. Also, FLAG_ADAPTER_POSITION_UNKNOWN is set as soon + * as adapter notification arrives vs FLAG_INVALID is set lazily before layout is + * re-calculated. + */ + static final int FLAG_ADAPTER_POSITION_UNKNOWN = 1 << 9; + + private int mFlags; + + private int mIsRecyclableCount = 0; + + // If non-null, view is currently considered scrap and may be reused for other data by the + // scrap container. + private Recycler mScrapContainer = null; + + /** + * Is set when VH is bound from the adapter and cleaned right before it is sent to + * {@link RecycledViewPool}. + */ + RecyclerView mOwnerRecyclerView; + + public ViewHolder(View itemView) { + if (itemView == null) { + throw new IllegalArgumentException("itemView may not be null"); + } + this.itemView = itemView; + } + + void flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout) { + addFlags(ViewHolder.FLAG_REMOVED); + offsetPosition(offset, applyToPreLayout); + mPosition = mNewPosition; + } + + void offsetPosition(int offset, boolean applyToPreLayout) { + if (mOldPosition == NO_POSITION) { + mOldPosition = mPosition; + } + if (mPreLayoutPosition == NO_POSITION) { + mPreLayoutPosition = mPosition; + } + if (applyToPreLayout) { + mPreLayoutPosition += offset; + } + mPosition += offset; + if (itemView.getLayoutParams() != null) { + ((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true; + } + } + + void clearOldPosition() { + mOldPosition = NO_POSITION; + mPreLayoutPosition = NO_POSITION; + } + + void saveOldPosition() { + if (mOldPosition == NO_POSITION) { + mOldPosition = mPosition; + } + } + + boolean shouldIgnore() { + return (mFlags & FLAG_IGNORE) != 0; + } + + /** + * @deprecated This method is deprecated because its meaning is ambiguous due to the async + * handling of adapter updates. Please use {@link #getLayoutPosition()} or + * {@link #getAdapterPosition()} depending on your use case. + * + * @see #getLayoutPosition() + * @see #getAdapterPosition() + */ + @Deprecated + public final int getPosition() { + return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition; + } + + /** + * Returns the position of the ViewHolder in terms of the latest layout pass. + *

+ * This position is mostly used by RecyclerView components to be consistent while + * RecyclerView lazily processes adapter updates. + *

+ * For performance and animation reasons, RecyclerView batches all adapter updates until the + * next layout pass. This may cause mismatches between the Adapter position of the item and + * the position it had in the latest layout calculations. + *

+ * LayoutManagers should always call this method while doing calculations based on item + * positions. All methods in {@link RecyclerView.LayoutManager}, {@link RecyclerView.State}, + * {@link RecyclerView.Recycler} that receive a position expect it to be the layout position + * of the item. + *

+ * If LayoutManager needs to call an external method that requires the adapter position of + * the item, it can use {@link #getAdapterPosition()} or + * {@link RecyclerView.Recycler#convertPreLayoutPositionToPostLayout(int)}. + * + * @return Returns the adapter position of the ViewHolder in the latest layout pass. + * @see #getAdapterPosition() + */ + public final int getLayoutPosition() { + return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition; + } + + /** + * Returns the Adapter position of the item represented by this ViewHolder. + *

+ * Note that this might be different than the {@link #getLayoutPosition()} if there are + * pending adapter updates but a new layout pass has not happened yet. + *

+ * RecyclerView does not handle any adapter updates until the next layout traversal. This + * may create temporary inconsistencies between what user sees on the screen and what + * adapter contents have. This inconsistency is not important since it will be less than + * 16ms but it might be a problem if you want to use ViewHolder position to access the + * adapter. Sometimes, you may need to get the exact adapter position to do + * some actions in response to user events. In that case, you should use this method which + * will calculate the Adapter position of the ViewHolder. + *

+ * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the + * next layout pass, the return value of this method will be {@link #NO_POSITION}. + * + * @return The adapter position of the item if it still exists in the adapter. + * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter, + * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last + * layout pass or the ViewHolder has already been recycled. + */ + public final int getAdapterPosition() { + if (mOwnerRecyclerView == null) { + return NO_POSITION; + } + return mOwnerRecyclerView.getAdapterPositionFor(this); + } + + /** + * When LayoutManager supports animations, RecyclerView tracks 3 positions for ViewHolders + * to perform animations. + *

+ * If a ViewHolder was laid out in the previous onLayout call, old position will keep its + * adapter index in the previous layout. + * + * @return The previous adapter index of the Item represented by this ViewHolder or + * {@link #NO_POSITION} if old position does not exists or cleared (pre-layout is + * complete). + */ + public final int getOldPosition() { + return mOldPosition; + } + + /** + * Returns The itemId represented by this ViewHolder. + * + * @return The the item's id if adapter has stable ids, {@link RecyclerView#NO_ID} + * otherwise + */ + public final long getItemId() { + return mItemId; + } + + /** + * @return The view type of this ViewHolder. + */ + public final int getItemViewType() { + return mItemViewType; + } + + boolean isScrap() { + return mScrapContainer != null; + } + + void unScrap() { + mScrapContainer.unscrapView(this); + } + + boolean wasReturnedFromScrap() { + return (mFlags & FLAG_RETURNED_FROM_SCRAP) != 0; + } + + void clearReturnedFromScrapFlag() { + mFlags = mFlags & ~FLAG_RETURNED_FROM_SCRAP; + } + + void clearTmpDetachFlag() { + mFlags = mFlags & ~FLAG_TMP_DETACHED; + } + + void stopIgnoring() { + mFlags = mFlags & ~FLAG_IGNORE; + } + + void setScrapContainer(Recycler recycler) { + mScrapContainer = recycler; + } + + boolean isInvalid() { + return (mFlags & FLAG_INVALID) != 0; + } + + boolean needsUpdate() { + return (mFlags & FLAG_UPDATE) != 0; + } + + boolean isChanged() { + return (mFlags & FLAG_CHANGED) != 0; + } + + boolean isBound() { + return (mFlags & FLAG_BOUND) != 0; + } + + boolean isRemoved() { + return (mFlags & FLAG_REMOVED) != 0; + } + + boolean hasAnyOfTheFlags(int flags) { + return (mFlags & flags) != 0; + } + + boolean isTmpDetached() { + return (mFlags & FLAG_TMP_DETACHED) != 0; + } + + boolean isAdapterPositionUnknown() { + return (mFlags & FLAG_ADAPTER_POSITION_UNKNOWN) != 0 || isInvalid(); + } + + void setFlags(int flags, int mask) { + mFlags = (mFlags & ~mask) | (flags & mask); + } + + void addFlags(int flags) { + mFlags |= flags; + } + + void resetInternal() { + mFlags = 0; + mPosition = NO_POSITION; + mOldPosition = NO_POSITION; + mItemId = NO_ID; + mPreLayoutPosition = NO_POSITION; + mIsRecyclableCount = 0; + mShadowedHolder = null; + mShadowingHolder = null; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ViewHolder{" + + Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId + + ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition); + if (isScrap()) sb.append(" scrap"); + if (isInvalid()) sb.append(" invalid"); + if (!isBound()) sb.append(" unbound"); + if (needsUpdate()) sb.append(" update"); + if (isRemoved()) sb.append(" removed"); + if (shouldIgnore()) sb.append(" ignored"); + if (isChanged()) sb.append(" changed"); + if (isTmpDetached()) sb.append(" tmpDetached"); + if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")"); + if (isAdapterPositionUnknown()) sb.append("undefined adapter position"); + + if (itemView.getParent() == null) sb.append(" no parent"); + sb.append("}"); + return sb.toString(); + } + + /** + * Informs the recycler whether this item can be recycled. Views which are not + * recyclable will not be reused for other items until setIsRecyclable() is + * later set to true. Calls to setIsRecyclable() should always be paired (one + * call to setIsRecyclabe(false) should always be matched with a later call to + * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally + * reference-counted. + * + * @param recyclable Whether this item is available to be recycled. Default value + * is true. + */ + public final void setIsRecyclable(boolean recyclable) { + mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1; + if (mIsRecyclableCount < 0) { + mIsRecyclableCount = 0; + if (DEBUG) { + throw new RuntimeException("isRecyclable decremented below 0: " + + "unmatched pair of setIsRecyable() calls for " + this); + } + Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: " + + "unmatched pair of setIsRecyable() calls for " + this); + } else if (!recyclable && mIsRecyclableCount == 1) { + mFlags |= FLAG_NOT_RECYCLABLE; + } else if (recyclable && mIsRecyclableCount == 0) { + mFlags &= ~FLAG_NOT_RECYCLABLE; + } + if (DEBUG) { + Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this); + } + } + + /** + * @see {@link #setIsRecyclable(boolean)} + * + * @return true if this item is available to be recycled, false otherwise. + */ + public final boolean isRecyclable() { + return (mFlags & FLAG_NOT_RECYCLABLE) == 0 && + !ViewCompat.hasTransientState(itemView); + } + + /** + * Returns whether we have animations referring to this view holder or not. + * This is similar to isRecyclable flag but does not check transient state. + */ + private boolean shouldBeKeptAsChild() { + return (mFlags & FLAG_NOT_RECYCLABLE) != 0; + } + + /** + * @return True if ViewHolder is not refenrenced by RecyclerView animations but has + * transient state which will prevent it from being recycled. + */ + private boolean doesTransientStatePreventRecycling() { + return (mFlags & FLAG_NOT_RECYCLABLE) == 0 && ViewCompat.hasTransientState(itemView); + } + } + + private int getAdapterPositionFor(ViewHolder viewHolder) { + if (viewHolder.hasAnyOfTheFlags( ViewHolder.FLAG_INVALID | + ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN) + || !viewHolder.isBound()) { + return RecyclerView.NO_POSITION; + } + return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition); + } + + /** + * {@link android.view.ViewGroup.MarginLayoutParams LayoutParams} subclass for children of + * {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged + * to create their own subclass of this LayoutParams class + * to store any additional required per-child view metadata about the layout. + */ + public static class LayoutParams extends android.view.ViewGroup.MarginLayoutParams { + ViewHolder mViewHolder; + final Rect mDecorInsets = new Rect(); + boolean mInsetsDirty = true; + // Flag is set to true if the view is bound while it is detached from RV. + // In this case, we need to manually call invalidate after view is added to guarantee that + // invalidation is populated through the View hierarchy + boolean mPendingInvalidate = false; + + public LayoutParams(Context c, AttributeSet attrs) { + super(c, attrs); + } + + public LayoutParams(int width, int height) { + super(width, height); + } + + public LayoutParams(MarginLayoutParams source) { + super(source); + } + + public LayoutParams(ViewGroup.LayoutParams source) { + super(source); + } + + public LayoutParams(LayoutParams source) { + super((ViewGroup.LayoutParams) source); + } + + /** + * Returns true if the view this LayoutParams is attached to needs to have its content + * updated from the corresponding adapter. + * + * @return true if the view should have its content updated + */ + public boolean viewNeedsUpdate() { + return mViewHolder.needsUpdate(); + } + + /** + * Returns true if the view this LayoutParams is attached to is now representing + * potentially invalid data. A LayoutManager should scrap/recycle it. + * + * @return true if the view is invalid + */ + public boolean isViewInvalid() { + return mViewHolder.isInvalid(); + } + + /** + * Returns true if the adapter data item corresponding to the view this LayoutParams + * is attached to has been removed from the data set. A LayoutManager may choose to + * treat it differently in order to animate its outgoing or disappearing state. + * + * @return true if the item the view corresponds to was removed from the data set + */ + public boolean isItemRemoved() { + return mViewHolder.isRemoved(); + } + + /** + * Returns true if the adapter data item corresponding to the view this LayoutParams + * is attached to has been changed in the data set. A LayoutManager may choose to + * treat it differently in order to animate its changing state. + * + * @return true if the item the view corresponds to was changed in the data set + */ + public boolean isItemChanged() { + return mViewHolder.isChanged(); + } + + /** + * @deprecated use {@link #getViewLayoutPosition()} or {@link #getViewAdapterPosition()} + */ + public int getViewPosition() { + return mViewHolder.getPosition(); + } + + /** + * Returns the adapter position that the view this LayoutParams is attached to corresponds + * to as of latest layout calculation. + * + * @return the adapter position this view as of latest layout pass + */ + public int getViewLayoutPosition() { + return mViewHolder.getLayoutPosition(); + } + + /** + * Returns the up-to-date adapter position that the view this LayoutParams is attached to + * corresponds to. + * + * @return the up-to-date adapter position this view. It may return + * {@link RecyclerView#NO_POSITION} if item represented by this View has been removed or + * its up-to-date position cannot be calculated. + */ + public int getViewAdapterPosition() { + return mViewHolder.getAdapterPosition(); + } + } + + /** + * Observer base class for watching changes to an {@link Adapter}. + * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}. + */ + public static abstract class AdapterDataObserver { + public void onChanged() { + // Do nothing + } + + public void onItemRangeChanged(int positionStart, int itemCount) { + // do nothing + } + + public void onItemRangeInserted(int positionStart, int itemCount) { + // do nothing + } + + public void onItemRangeRemoved(int positionStart, int itemCount) { + // do nothing + } + + public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { + // do nothing + } + } + + /** + *

Base class for smooth scrolling. Handles basic tracking of the target view position and + * provides methods to trigger a programmatic scroll.

+ * + * @see LinearSmoothScroller + */ + public static abstract class SmoothScroller { + + private int mTargetPosition = RecyclerView.NO_POSITION; + + private RecyclerView mRecyclerView; + + private LayoutManager mLayoutManager; + + private boolean mPendingInitialRun; + + private boolean mRunning; + + private View mTargetView; + + private final Action mRecyclingAction; + + public SmoothScroller() { + mRecyclingAction = new Action(0, 0); + } + + /** + * Starts a smooth scroll for the given target position. + *

In each animation step, {@link RecyclerView} will check + * for the target view and call either + * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or + * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)} until + * SmoothScroller is stopped.

+ * + *

Note that if RecyclerView finds the target view, it will automatically stop the + * SmoothScroller. This does not mean that scroll will stop, it only means it will + * stop calling SmoothScroller in each animation step.

+ */ + void start(RecyclerView recyclerView, LayoutManager layoutManager) { + mRecyclerView = recyclerView; + mLayoutManager = layoutManager; + if (mTargetPosition == RecyclerView.NO_POSITION) { + throw new IllegalArgumentException("Invalid target position"); + } + mRecyclerView.mState.mTargetPosition = mTargetPosition; + mRunning = true; + mPendingInitialRun = true; + mTargetView = findViewByPosition(getTargetPosition()); + onStart(); + mRecyclerView.mViewFlinger.postOnAnimation(); + } + + public void setTargetPosition(int targetPosition) { + mTargetPosition = targetPosition; + } + + /** + * @return The LayoutManager to which this SmoothScroller is attached + */ + public LayoutManager getLayoutManager() { + return mLayoutManager; + } + + /** + * Stops running the SmoothScroller in each animation callback. Note that this does not + * cancel any existing {@link Action} updated by + * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or + * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)}. + */ + final protected void stop() { + if (!mRunning) { + return; + } + onStop(); + mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION; + mTargetView = null; + mTargetPosition = RecyclerView.NO_POSITION; + mPendingInitialRun = false; + mRunning = false; + // trigger a cleanup + mLayoutManager.onSmoothScrollerStopped(this); + // clear references to avoid any potential leak by a custom smooth scroller + mLayoutManager = null; + mRecyclerView = null; + } + + /** + * Returns true if SmoothScroller has been started but has not received the first + * animation + * callback yet. + * + * @return True if this SmoothScroller is waiting to start + */ + public boolean isPendingInitialRun() { + return mPendingInitialRun; + } + + + /** + * @return True if SmoothScroller is currently active + */ + public boolean isRunning() { + return mRunning; + } + + /** + * Returns the adapter position of the target item + * + * @return Adapter position of the target item or + * {@link RecyclerView#NO_POSITION} if no target view is set. + */ + public int getTargetPosition() { + return mTargetPosition; + } + + private void onAnimation(int dx, int dy) { + if (!mRunning || mTargetPosition == RecyclerView.NO_POSITION) { + stop(); + } + mPendingInitialRun = false; + if (mTargetView != null) { + // verify target position + if (getChildPosition(mTargetView) == mTargetPosition) { + onTargetFound(mTargetView, mRecyclerView.mState, mRecyclingAction); + mRecyclingAction.runIfNecessary(mRecyclerView); + stop(); + } else { + Log.e(TAG, "Passed over target position while smooth scrolling."); + mTargetView = null; + } + } + if (mRunning) { + onSeekTargetStep(dx, dy, mRecyclerView.mState, mRecyclingAction); + mRecyclingAction.runIfNecessary(mRecyclerView); + } + } + + /** + * @see RecyclerView#getChildLayoutPosition(android.view.View) + */ + public int getChildPosition(View view) { + return mRecyclerView.getChildLayoutPosition(view); + } + + /** + * @see RecyclerView.LayoutManager#getChildCount() + */ + public int getChildCount() { + return mRecyclerView.mLayout.getChildCount(); + } + + /** + * @see RecyclerView.LayoutManager#findViewByPosition(int) + */ + public View findViewByPosition(int position) { + return mRecyclerView.mLayout.findViewByPosition(position); + } + + /** + * @see RecyclerView#scrollToPosition(int) + */ + public void instantScrollToPosition(int position) { + mRecyclerView.scrollToPosition(position); + } + + protected void onChildAttachedToWindow(View child) { + if (getChildPosition(child) == getTargetPosition()) { + mTargetView = child; + if (DEBUG) { + Log.d(TAG, "smooth scroll target view has been attached"); + } + } + } + + /** + * Normalizes the vector. + * @param scrollVector The vector that points to the target scroll position + */ + protected void normalize(PointF scrollVector) { + final double magnitute = Math.sqrt(scrollVector.x * scrollVector.x + scrollVector.y * + scrollVector.y); + scrollVector.x /= magnitute; + scrollVector.y /= magnitute; + } + + /** + * Called when smooth scroll is started. This might be a good time to do setup. + */ + abstract protected void onStart(); + + /** + * Called when smooth scroller is stopped. This is a good place to cleanup your state etc. + * @see #stop() + */ + abstract protected void onStop(); + + /** + *

RecyclerView will call this method each time it scrolls until it can find the target + * position in the layout.

+ *

SmoothScroller should check dx, dy and if scroll should be changed, update the + * provided {@link Action} to define the next scroll.

+ * + * @param dx Last scroll amount horizontally + * @param dy Last scroll amount verticaully + * @param state Transient state of RecyclerView + * @param action If you want to trigger a new smooth scroll and cancel the previous one, + * update this object. + */ + abstract protected void onSeekTargetStep(int dx, int dy, State state, Action action); + + /** + * Called when the target position is laid out. This is the last callback SmoothScroller + * will receive and it should update the provided {@link Action} to define the scroll + * details towards the target view. + * @param targetView The view element which render the target position. + * @param state Transient state of RecyclerView + * @param action Action instance that you should update to define final scroll action + * towards the targetView + */ + abstract protected void onTargetFound(View targetView, State state, Action action); + + /** + * Holds information about a smooth scroll request by a {@link SmoothScroller}. + */ + public static class Action { + + public static final int UNDEFINED_DURATION = Integer.MIN_VALUE; + + private int mDx; + + private int mDy; + + private int mDuration; + + private Interpolator mInterpolator; + + private boolean changed = false; + + // we track this variable to inform custom implementer if they are updating the action + // in every animation callback + private int consecutiveUpdates = 0; + + /** + * @param dx Pixels to scroll horizontally + * @param dy Pixels to scroll vertically + */ + public Action(int dx, int dy) { + this(dx, dy, UNDEFINED_DURATION, null); + } + + /** + * @param dx Pixels to scroll horizontally + * @param dy Pixels to scroll vertically + * @param duration Duration of the animation in milliseconds + */ + public Action(int dx, int dy, int duration) { + this(dx, dy, duration, null); + } + + /** + * @param dx Pixels to scroll horizontally + * @param dy Pixels to scroll vertically + * @param duration Duration of the animation in milliseconds + * @param interpolator Interpolator to be used when calculating scroll position in each + * animation step + */ + public Action(int dx, int dy, int duration, Interpolator interpolator) { + mDx = dx; + mDy = dy; + mDuration = duration; + mInterpolator = interpolator; + } + private void runIfNecessary(RecyclerView recyclerView) { + if (changed) { + validate(); + if (mInterpolator == null) { + if (mDuration == UNDEFINED_DURATION) { + recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy); + } else { + recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration); + } + } else { + recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration, mInterpolator); + } + consecutiveUpdates ++; + if (consecutiveUpdates > 10) { + // A new action is being set in every animation step. This looks like a bad + // implementation. Inform developer. + Log.e(TAG, "Smooth Scroll action is being updated too frequently. Make sure" + + " you are not changing it unless necessary"); + } + changed = false; + } else { + consecutiveUpdates = 0; + } + } + + private void validate() { + if (mInterpolator != null && mDuration < 1) { + throw new IllegalStateException("If you provide an interpolator, you must" + + " set a positive duration"); + } else if (mDuration < 1) { + throw new IllegalStateException("Scroll duration must be a positive number"); + } + } + + public int getDx() { + return mDx; + } + + public void setDx(int dx) { + changed = true; + mDx = dx; + } + + public int getDy() { + return mDy; + } + + public void setDy(int dy) { + changed = true; + mDy = dy; + } + + public int getDuration() { + return mDuration; + } + + public void setDuration(int duration) { + changed = true; + mDuration = duration; + } + + public Interpolator getInterpolator() { + return mInterpolator; + } + + /** + * Sets the interpolator to calculate scroll steps + * @param interpolator The interpolator to use. If you specify an interpolator, you must + * also set the duration. + * @see #setDuration(int) + */ + public void setInterpolator(Interpolator interpolator) { + changed = true; + mInterpolator = interpolator; + } + + /** + * Updates the action with given parameters. + * @param dx Pixels to scroll horizontally + * @param dy Pixels to scroll vertically + * @param duration Duration of the animation in milliseconds + * @param interpolator Interpolator to be used when calculating scroll position in each + * animation step + */ + public void update(int dx, int dy, int duration, Interpolator interpolator) { + mDx = dx; + mDy = dy; + mDuration = duration; + mInterpolator = interpolator; + changed = true; + } + } + } + + static class AdapterDataObservable extends Observable { + public boolean hasObservers() { + return !mObservers.isEmpty(); + } + + public void notifyChanged() { + // since onChanged() is implemented by the app, it could do anything, including + // removing itself from {@link mObservers} - and that could cause problems if + // an iterator is used on the ArrayList {@link mObservers}. + // to avoid such problems, just march thru the list in the reverse order. + for (int i = mObservers.size() - 1; i >= 0; i--) { + mObservers.get(i).onChanged(); + } + } + + public void notifyItemRangeChanged(int positionStart, int itemCount) { + // since onItemRangeChanged() is implemented by the app, it could do anything, including + // removing itself from {@link mObservers} - and that could cause problems if + // an iterator is used on the ArrayList {@link mObservers}. + // to avoid such problems, just march thru the list in the reverse order. + for (int i = mObservers.size() - 1; i >= 0; i--) { + mObservers.get(i).onItemRangeChanged(positionStart, itemCount); + } + } + + public void notifyItemRangeInserted(int positionStart, int itemCount) { + // since onItemRangeInserted() is implemented by the app, it could do anything, + // including removing itself from {@link mObservers} - and that could cause problems if + // an iterator is used on the ArrayList {@link mObservers}. + // to avoid such problems, just march thru the list in the reverse order. + for (int i = mObservers.size() - 1; i >= 0; i--) { + mObservers.get(i).onItemRangeInserted(positionStart, itemCount); + } + } + + public void notifyItemRangeRemoved(int positionStart, int itemCount) { + // since onItemRangeRemoved() is implemented by the app, it could do anything, including + // removing itself from {@link mObservers} - and that could cause problems if + // an iterator is used on the ArrayList {@link mObservers}. + // to avoid such problems, just march thru the list in the reverse order. + for (int i = mObservers.size() - 1; i >= 0; i--) { + mObservers.get(i).onItemRangeRemoved(positionStart, itemCount); + } + } + + public void notifyItemMoved(int fromPosition, int toPosition) { + for (int i = mObservers.size() - 1; i >= 0; i--) { + mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1); + } + } + } + + static class SavedState extends android.view.View.BaseSavedState { + + Parcelable mLayoutState; + + /** + * called by CREATOR + */ + SavedState(Parcel in) { + super(in); + mLayoutState = in.readParcelable(LayoutManager.class.getClassLoader()); + } + + /** + * Called by onSaveInstanceState + */ + SavedState(Parcelable superState) { + super(superState); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeParcelable(mLayoutState, 0); + } + + private void copyFrom(SavedState other) { + mLayoutState = other.mLayoutState; + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + @Override + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } + /** + *

Contains useful information about the current RecyclerView state like target scroll + * position or view focus. State object can also keep arbitrary data, identified by resource + * ids.

+ *

Often times, RecyclerView components will need to pass information between each other. + * To provide a well defined data bus between components, RecyclerView passes the same State + * object to component callbacks and these components can use it to exchange data.

+ *

If you implement custom components, you can use State's put/get/remove methods to pass + * data between your components without needing to manage their lifecycles.

+ */ + public static class State { + + private int mTargetPosition = RecyclerView.NO_POSITION; + ArrayMap mPreLayoutHolderMap = + new ArrayMap(); + ArrayMap mPostLayoutHolderMap = + new ArrayMap(); + // nullable + ArrayMap mOldChangedHolders = new ArrayMap(); + + // we use this like a set + final List mDisappearingViewsInLayoutPass = new ArrayList(); + + private SparseArray mData; + + /** + * Number of items adapter has. + */ + int mItemCount = 0; + + /** + * Number of items adapter had in the previous layout. + */ + private int mPreviousLayoutItemCount = 0; + + /** + * Number of items that were NOT laid out but has been deleted from the adapter after the + * previous layout. + */ + private int mDeletedInvisibleItemCountSincePreviousLayout = 0; + + private boolean mStructureChanged = false; + + private boolean mInPreLayout = false; + + private boolean mRunSimpleAnimations = false; + + private boolean mRunPredictiveAnimations = false; + + State reset() { + mTargetPosition = RecyclerView.NO_POSITION; + if (mData != null) { + mData.clear(); + } + mItemCount = 0; + mStructureChanged = false; + return this; + } + + public boolean isPreLayout() { + return mInPreLayout; + } + + /** + * Returns whether RecyclerView will run predictive animations in this layout pass + * or not. + * + * @return true if RecyclerView is calculating predictive animations to be run at the end + * of the layout pass. + */ + public boolean willRunPredictiveAnimations() { + return mRunPredictiveAnimations; + } + + /** + * Returns whether RecyclerView will run simple animations in this layout pass + * or not. + * + * @return true if RecyclerView is calculating simple animations to be run at the end of + * the layout pass. + */ + public boolean willRunSimpleAnimations() { + return mRunSimpleAnimations; + } + + /** + * Removes the mapping from the specified id, if there was any. + * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.* to + * preserve cross functionality and avoid conflicts. + */ + public void remove(int resourceId) { + if (mData == null) { + return; + } + mData.remove(resourceId); + } + + /** + * Gets the Object mapped from the specified id, or null + * if no such data exists. + * + * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.* + * to + * preserve cross functionality and avoid conflicts. + */ + public T get(int resourceId) { + if (mData == null) { + return null; + } + return (T) mData.get(resourceId); + } + + /** + * Adds a mapping from the specified id to the specified value, replacing the previous + * mapping from the specified key if there was one. + * + * @param resourceId Id of the resource you want to add. It is suggested to use R.id.* to + * preserve cross functionality and avoid conflicts. + * @param data The data you want to associate with the resourceId. + */ + public void put(int resourceId, Object data) { + if (mData == null) { + mData = new SparseArray(); + } + mData.put(resourceId, data); + } + + /** + * If scroll is triggered to make a certain item visible, this value will return the + * adapter index of that item. + * @return Adapter index of the target item or + * {@link RecyclerView#NO_POSITION} if there is no target + * position. + */ + public int getTargetScrollPosition() { + return mTargetPosition; + } + + /** + * Returns if current scroll has a target position. + * @return true if scroll is being triggered to make a certain position visible + * @see #getTargetScrollPosition() + */ + public boolean hasTargetScrollPosition() { + return mTargetPosition != RecyclerView.NO_POSITION; + } + + /** + * @return true if the structure of the data set has changed since the last call to + * onLayoutChildren, false otherwise + */ + public boolean didStructureChange() { + return mStructureChanged; + } + + /** + * Returns the total number of items that can be laid out. Note that this number is not + * necessarily equal to the number of items in the adapter, so you should always use this + * number for your position calculations and never access the adapter directly. + *

+ * RecyclerView listens for Adapter's notify events and calculates the effects of adapter + * data changes on existing Views. These calculations are used to decide which animations + * should be run. + *

+ * To support predictive animations, RecyclerView may rewrite or reorder Adapter changes to + * present the correct state to LayoutManager in pre-layout pass. + *

+ * For example, a newly added item is not included in pre-layout item count because + * pre-layout reflects the contents of the adapter before the item is added. Behind the + * scenes, RecyclerView offsets {@link Recycler#getViewForPosition(int)} calls such that + * LayoutManager does not know about the new item's existence in pre-layout. The item will + * be available in second layout pass and will be included in the item count. Similar + * adjustments are made for moved and removed items as well. + *

+ * You can get the adapter's item count via {@link LayoutManager#getItemCount()} method. + * + * @return The number of items currently available + * @see LayoutManager#getItemCount() + */ + public int getItemCount() { + return mInPreLayout ? + (mPreviousLayoutItemCount - mDeletedInvisibleItemCountSincePreviousLayout) : + mItemCount; + } + + void onViewRecycled(ViewHolder holder) { + mPreLayoutHolderMap.remove(holder); + mPostLayoutHolderMap.remove(holder); + if (mOldChangedHolders != null) { + removeFrom(mOldChangedHolders, holder); + } + mDisappearingViewsInLayoutPass.remove(holder.itemView); + // holder cannot be in new list. + } + + public void onViewIgnored(ViewHolder holder) { + onViewRecycled(holder); + } + + private void removeFrom(ArrayMap holderMap, ViewHolder holder) { + for (int i = holderMap.size() - 1; i >= 0; i --) { + if (holder == holderMap.valueAt(i)) { + holderMap.removeAt(i); + return; + } + } + } + + void removeFromDisappearingList(View child) { + mDisappearingViewsInLayoutPass.remove(child); + } + + void addToDisappearingList(View child) { + if (!mDisappearingViewsInLayoutPass.contains(child)) { + mDisappearingViewsInLayoutPass.add(child); + } + } + + @Override + public String toString() { + return "State{" + + "mTargetPosition=" + mTargetPosition + + ", mPreLayoutHolderMap=" + mPreLayoutHolderMap + + ", mPostLayoutHolderMap=" + mPostLayoutHolderMap + + ", mData=" + mData + + ", mItemCount=" + mItemCount + + ", mPreviousLayoutItemCount=" + mPreviousLayoutItemCount + + ", mDeletedInvisibleItemCountSincePreviousLayout=" + + mDeletedInvisibleItemCountSincePreviousLayout + + ", mStructureChanged=" + mStructureChanged + + ", mInPreLayout=" + mInPreLayout + + ", mRunSimpleAnimations=" + mRunSimpleAnimations + + ", mRunPredictiveAnimations=" + mRunPredictiveAnimations + + '}'; + } + } + + /** + * Internal listener that manages items after animations finish. This is how items are + * retained (not recycled) during animations, but allowed to be recycled afterwards. + * It depends on the contract with the ItemAnimator to call the appropriate dispatch*Finished() + * method on the animator's listener when it is done animating any item. + */ + private class ItemAnimatorRestoreListener implements ItemAnimator.ItemAnimatorListener { + + @Override + public void onRemoveFinished(ViewHolder item) { + item.setIsRecyclable(true); + if (!removeAnimatingView(item.itemView) && item.isTmpDetached()) { + removeDetachedView(item.itemView, false); + } + } + + @Override + public void onAddFinished(ViewHolder item) { + item.setIsRecyclable(true); + if (!item.shouldBeKeptAsChild()) { + removeAnimatingView(item.itemView); + } + } + + @Override + public void onMoveFinished(ViewHolder item) { + item.setIsRecyclable(true); + if (!item.shouldBeKeptAsChild()) { + removeAnimatingView(item.itemView); + } + } + + @Override + public void onChangeFinished(ViewHolder item) { + item.setIsRecyclable(true); + /** + * We check both shadowed and shadowing because a ViewHolder may get both roles at the + * same time. + * + * Assume this flow: + * item X is represented by VH_1. Then itemX changes, so we create VH_2 . + * RV sets the following and calls item animator: + * VH_1.shadowed = VH_2; + * VH_1.mChanged = true; + * VH_2.shadowing =VH_1; + * + * Then, before the first change finishes, item changes again so we create VH_3. + * RV sets the following and calls item animator: + * VH_2.shadowed = VH_3 + * VH_2.mChanged = true + * VH_3.shadowing = VH_2 + * + * Because VH_2 already has an animation, it will be cancelled. At this point VH_2 has + * both shadowing and shadowed fields set. Shadowing information is obsolete now + * because the first animation where VH_2 is newViewHolder is not valid anymore. + * We ended up in this case because VH_2 played both roles. On the other hand, + * we DO NOT want to clear its changed flag. + * + * If second change was simply reverting first change, we would find VH_1 in + * {@link Recycler#getScrapViewForPosition(int, int, boolean)} and recycle it before + * re-using + */ + if (item.mShadowedHolder != null && item.mShadowingHolder == null) { // old vh + item.mShadowedHolder = null; + item.setFlags(~ViewHolder.FLAG_CHANGED, item.mFlags); + } + // always null this because an OldViewHolder can never become NewViewHolder w/o being + // recycled. + item.mShadowingHolder = null; + if (!item.shouldBeKeptAsChild()) { + removeAnimatingView(item.itemView); + } + } + }; + + /** + * This class defines the animations that take place on items as changes are made + * to the adapter. + * + * Subclasses of ItemAnimator can be used to implement custom animations for actions on + * ViewHolder items. The RecyclerView will manage retaining these items while they + * are being animated, but implementors must call the appropriate "Starting" + * ({@link #dispatchRemoveStarting(ViewHolder)}, {@link #dispatchMoveStarting(ViewHolder)}, + * {@link #dispatchChangeStarting(ViewHolder, boolean)}, or + * {@link #dispatchAddStarting(ViewHolder)}) + * and "Finished" ({@link #dispatchRemoveFinished(ViewHolder)}, + * {@link #dispatchMoveFinished(ViewHolder)}, + * {@link #dispatchChangeFinished(ViewHolder, boolean)}, + * or {@link #dispatchAddFinished(ViewHolder)}) methods when each item animation is + * being started and ended. + * + *

By default, RecyclerView uses {@link DefaultItemAnimator}

+ * + * @see #setItemAnimator(ItemAnimator) + */ + public static abstract class ItemAnimator { + + private ItemAnimatorListener mListener = null; + private ArrayList mFinishedListeners = + new ArrayList(); + + private long mAddDuration = 120; + private long mRemoveDuration = 120; + private long mMoveDuration = 250; + private long mChangeDuration = 250; + + private boolean mSupportsChangeAnimations = true; + + /** + * Gets the current duration for which all move animations will run. + * + * @return The current move duration + */ + public long getMoveDuration() { + return mMoveDuration; + } + + /** + * Sets the duration for which all move animations will run. + * + * @param moveDuration The move duration + */ + public void setMoveDuration(long moveDuration) { + mMoveDuration = moveDuration; + } + + /** + * Gets the current duration for which all add animations will run. + * + * @return The current add duration + */ + public long getAddDuration() { + return mAddDuration; + } + + /** + * Sets the duration for which all add animations will run. + * + * @param addDuration The add duration + */ + public void setAddDuration(long addDuration) { + mAddDuration = addDuration; + } + + /** + * Gets the current duration for which all remove animations will run. + * + * @return The current remove duration + */ + public long getRemoveDuration() { + return mRemoveDuration; + } + + /** + * Sets the duration for which all remove animations will run. + * + * @param removeDuration The remove duration + */ + public void setRemoveDuration(long removeDuration) { + mRemoveDuration = removeDuration; + } + + /** + * Gets the current duration for which all change animations will run. + * + * @return The current change duration + */ + public long getChangeDuration() { + return mChangeDuration; + } + + /** + * Sets the duration for which all change animations will run. + * + * @param changeDuration The change duration + */ + public void setChangeDuration(long changeDuration) { + mChangeDuration = changeDuration; + } + + /** + * Returns whether this ItemAnimator supports animations of change events. + * + * @return true if change animations are supported, false otherwise + */ + public boolean getSupportsChangeAnimations() { + return mSupportsChangeAnimations; + } + + /** + * Sets whether this ItemAnimator supports animations of item change events. + * If you set this property to false, actions on the data set which change the + * contents of items will not be animated. What those animations are is left + * up to the discretion of the ItemAnimator subclass, in its + * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} implementation. + * The value of this property is true by default. + * + * @see Adapter#notifyItemChanged(int) + * @see Adapter#notifyItemRangeChanged(int, int) + * + * @param supportsChangeAnimations true if change animations are supported by + * this ItemAnimator, false otherwise. If the property is false, the ItemAnimator + * will not receive a call to + * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} when changes occur. + */ + public void setSupportsChangeAnimations(boolean supportsChangeAnimations) { + mSupportsChangeAnimations = supportsChangeAnimations; + } + + /** + * Internal only: + * Sets the listener that must be called when the animator is finished + * animating the item (or immediately if no animation happens). This is set + * internally and is not intended to be set by external code. + * + * @param listener The listener that must be called. + */ + void setListener(ItemAnimatorListener listener) { + mListener = listener; + } + + /** + * Called when there are pending animations waiting to be started. This state + * is governed by the return values from {@link #animateAdd(ViewHolder) animateAdd()}, + * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()}, and + * {@link #animateRemove(ViewHolder) animateRemove()}, which inform the + * RecyclerView that the ItemAnimator wants to be called later to start the + * associated animations. runPendingAnimations() will be scheduled to be run + * on the next frame. + */ + abstract public void runPendingAnimations(); + + /** + * Called when an item is removed from the RecyclerView. Implementors can choose + * whether and how to animate that change, but must always call + * {@link #dispatchRemoveFinished(ViewHolder)} when done, either + * immediately (if no animation will occur) or after the animation actually finishes. + * The return value indicates whether an animation has been set up and whether the + * ItemAnimator's {@link #runPendingAnimations()} method should be called at the + * next opportunity. This mechanism allows ItemAnimator to set up individual animations + * as separate calls to {@link #animateAdd(ViewHolder) animateAdd()}, + * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()}, + * {@link #animateRemove(ViewHolder) animateRemove()}, and + * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} come in one by one, + * then start the animations together in the later call to {@link #runPendingAnimations()}. + * + *

This method may also be called for disappearing items which continue to exist in the + * RecyclerView, but for which the system does not have enough information to animate + * them out of view. In that case, the default animation for removing items is run + * on those items as well.

+ * + * @param holder The item that is being removed. + * @return true if a later call to {@link #runPendingAnimations()} is requested, + * false otherwise. + */ + abstract public boolean animateRemove(ViewHolder holder); + + /** + * Called when an item is added to the RecyclerView. Implementors can choose + * whether and how to animate that change, but must always call + * {@link #dispatchAddFinished(ViewHolder)} when done, either + * immediately (if no animation will occur) or after the animation actually finishes. + * The return value indicates whether an animation has been set up and whether the + * ItemAnimator's {@link #runPendingAnimations()} method should be called at the + * next opportunity. This mechanism allows ItemAnimator to set up individual animations + * as separate calls to {@link #animateAdd(ViewHolder) animateAdd()}, + * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()}, + * {@link #animateRemove(ViewHolder) animateRemove()}, and + * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} come in one by one, + * then start the animations together in the later call to {@link #runPendingAnimations()}. + * + *

This method may also be called for appearing items which were already in the + * RecyclerView, but for which the system does not have enough information to animate + * them into view. In that case, the default animation for adding items is run + * on those items as well.

+ * + * @param holder The item that is being added. + * @return true if a later call to {@link #runPendingAnimations()} is requested, + * false otherwise. + */ + abstract public boolean animateAdd(ViewHolder holder); + + /** + * Called when an item is moved in the RecyclerView. Implementors can choose + * whether and how to animate that change, but must always call + * {@link #dispatchMoveFinished(ViewHolder)} when done, either + * immediately (if no animation will occur) or after the animation actually finishes. + * The return value indicates whether an animation has been set up and whether the + * ItemAnimator's {@link #runPendingAnimations()} method should be called at the + * next opportunity. This mechanism allows ItemAnimator to set up individual animations + * as separate calls to {@link #animateAdd(ViewHolder) animateAdd()}, + * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()}, + * {@link #animateRemove(ViewHolder) animateRemove()}, and + * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} come in one by one, + * then start the animations together in the later call to {@link #runPendingAnimations()}. + * + * @param holder The item that is being moved. + * @return true if a later call to {@link #runPendingAnimations()} is requested, + * false otherwise. + */ + abstract public boolean animateMove(ViewHolder holder, int fromX, int fromY, + int toX, int toY); + + /** + * Called when an item is changed in the RecyclerView, as indicated by a call to + * {@link Adapter#notifyItemChanged(int)} or + * {@link Adapter#notifyItemRangeChanged(int, int)}. + *

+ * Implementers can choose whether and how to animate changes, but must always call + * {@link #dispatchChangeFinished(ViewHolder, boolean)} for each non-null ViewHolder, + * either immediately (if no animation will occur) or after the animation actually finishes. + * The return value indicates whether an animation has been set up and whether the + * ItemAnimator's {@link #runPendingAnimations()} method should be called at the + * next opportunity. This mechanism allows ItemAnimator to set up individual animations + * as separate calls to {@link #animateAdd(ViewHolder) animateAdd()}, + * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()}, + * {@link #animateRemove(ViewHolder) animateRemove()}, and + * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} come in one by one, + * then start the animations together in the later call to {@link #runPendingAnimations()}. + * + * @param oldHolder The original item that changed. + * @param newHolder The new item that was created with the changed content. Might be null + * @param fromLeft Left of the old view holder + * @param fromTop Top of the old view holder + * @param toLeft Left of the new view holder + * @param toTop Top of the new view holder + * @return true if a later call to {@link #runPendingAnimations()} is requested, + * false otherwise. + */ + abstract public boolean animateChange(ViewHolder oldHolder, + ViewHolder newHolder, int fromLeft, int fromTop, int toLeft, int toTop); + + + /** + * Method to be called by subclasses when a remove animation is done. + * + * @param item The item which has been removed + */ + public final void dispatchRemoveFinished(ViewHolder item) { + onRemoveFinished(item); + if (mListener != null) { + mListener.onRemoveFinished(item); + } + } + + /** + * Method to be called by subclasses when a move animation is done. + * + * @param item The item which has been moved + */ + public final void dispatchMoveFinished(ViewHolder item) { + onMoveFinished(item); + if (mListener != null) { + mListener.onMoveFinished(item); + } + } + + /** + * Method to be called by subclasses when an add animation is done. + * + * @param item The item which has been added + */ + public final void dispatchAddFinished(ViewHolder item) { + onAddFinished(item); + if (mListener != null) { + mListener.onAddFinished(item); + } + } + + /** + * Method to be called by subclasses when a change animation is done. + * + * @see #animateChange(ViewHolder, ViewHolder, int, int, int, int) + * @param item The item which has been changed (this method must be called for + * each non-null ViewHolder passed into + * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)}). + * @param oldItem true if this is the old item that was changed, false if + * it is the new item that replaced the old item. + */ + public final void dispatchChangeFinished(ViewHolder item, boolean oldItem) { + onChangeFinished(item, oldItem); + if (mListener != null) { + mListener.onChangeFinished(item); + } + } + + /** + * Method to be called by subclasses when a remove animation is being started. + * + * @param item The item being removed + */ + public final void dispatchRemoveStarting(ViewHolder item) { + onRemoveStarting(item); + } + + /** + * Method to be called by subclasses when a move animation is being started. + * + * @param item The item being moved + */ + public final void dispatchMoveStarting(ViewHolder item) { + onMoveStarting(item); + } + + /** + * Method to be called by subclasses when an add animation is being started. + * + * @param item The item being added + */ + public final void dispatchAddStarting(ViewHolder item) { + onAddStarting(item); + } + + /** + * Method to be called by subclasses when a change animation is being started. + * + * @param item The item which has been changed (this method must be called for + * each non-null ViewHolder passed into + * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)}). + * @param oldItem true if this is the old item that was changed, false if + * it is the new item that replaced the old item. + */ + public final void dispatchChangeStarting(ViewHolder item, boolean oldItem) { + onChangeStarting(item, oldItem); + } + + /** + * Method called when an animation on a view should be ended immediately. + * This could happen when other events, like scrolling, occur, so that + * animating views can be quickly put into their proper end locations. + * Implementations should ensure that any animations running on the item + * are canceled and affected properties are set to their end values. + * Also, appropriate dispatch methods (e.g., {@link #dispatchAddFinished(ViewHolder)} + * should be called since the animations are effectively done when this + * method is called. + * + * @param item The item for which an animation should be stopped. + */ + abstract public void endAnimation(ViewHolder item); + + /** + * Method called when all item animations should be ended immediately. + * This could happen when other events, like scrolling, occur, so that + * animating views can be quickly put into their proper end locations. + * Implementations should ensure that any animations running on any items + * are canceled and affected properties are set to their end values. + * Also, appropriate dispatch methods (e.g., {@link #dispatchAddFinished(ViewHolder)} + * should be called since the animations are effectively done when this + * method is called. + */ + abstract public void endAnimations(); + + /** + * Method which returns whether there are any item animations currently running. + * This method can be used to determine whether to delay other actions until + * animations end. + * + * @return true if there are any item animations currently running, false otherwise. + */ + abstract public boolean isRunning(); + + /** + * Like {@link #isRunning()}, this method returns whether there are any item + * animations currently running. Addtionally, the listener passed in will be called + * when there are no item animations running, either immediately (before the method + * returns) if no animations are currently running, or when the currently running + * animations are {@link #dispatchAnimationsFinished() finished}. + * + *

Note that the listener is transient - it is either called immediately and not + * stored at all, or stored only until it is called when running animations + * are finished sometime later.

+ * + * @param listener A listener to be called immediately if no animations are running + * or later when currently-running animations have finished. A null listener is + * equivalent to calling {@link #isRunning()}. + * @return true if there are any item animations currently running, false otherwise. + */ + public final boolean isRunning(ItemAnimatorFinishedListener listener) { + boolean running = isRunning(); + if (listener != null) { + if (!running) { + listener.onAnimationsFinished(); + } else { + mFinishedListeners.add(listener); + } + } + return running; + } + + /** + * The interface to be implemented by listeners to animation events from this + * ItemAnimator. This is used internally and is not intended for developers to + * create directly. + */ + interface ItemAnimatorListener { + void onRemoveFinished(ViewHolder item); + void onAddFinished(ViewHolder item); + void onMoveFinished(ViewHolder item); + void onChangeFinished(ViewHolder item); + } + + /** + * This method should be called by ItemAnimator implementations to notify + * any listeners that all pending and active item animations are finished. + */ + public final void dispatchAnimationsFinished() { + final int count = mFinishedListeners.size(); + for (int i = 0; i < count; ++i) { + mFinishedListeners.get(i).onAnimationsFinished(); + } + mFinishedListeners.clear(); + } + + /** + * This interface is used to inform listeners when all pending or running animations + * in an ItemAnimator are finished. This can be used, for example, to delay an action + * in a data set until currently-running animations are complete. + * + * @see #isRunning(ItemAnimatorFinishedListener) + */ + public interface ItemAnimatorFinishedListener { + void onAnimationsFinished(); + } + + /** + * Called when a remove animation is being started on the given ViewHolder. + * The default implementation does nothing. Subclasses may wish to override + * this method to handle any ViewHolder-specific operations linked to animation + * lifecycles. + * + * @param item The ViewHolder being animated. + */ + public void onRemoveStarting(ViewHolder item) {} + + /** + * Called when a remove animation has ended on the given ViewHolder. + * The default implementation does nothing. Subclasses may wish to override + * this method to handle any ViewHolder-specific operations linked to animation + * lifecycles. + * + * @param item The ViewHolder being animated. + */ + public void onRemoveFinished(ViewHolder item) {} + + /** + * Called when an add animation is being started on the given ViewHolder. + * The default implementation does nothing. Subclasses may wish to override + * this method to handle any ViewHolder-specific operations linked to animation + * lifecycles. + * + * @param item The ViewHolder being animated. + */ + public void onAddStarting(ViewHolder item) {} + + /** + * Called when an add animation has ended on the given ViewHolder. + * The default implementation does nothing. Subclasses may wish to override + * this method to handle any ViewHolder-specific operations linked to animation + * lifecycles. + * + * @param item The ViewHolder being animated. + */ + public void onAddFinished(ViewHolder item) {} + + /** + * Called when a move animation is being started on the given ViewHolder. + * The default implementation does nothing. Subclasses may wish to override + * this method to handle any ViewHolder-specific operations linked to animation + * lifecycles. + * + * @param item The ViewHolder being animated. + */ + public void onMoveStarting(ViewHolder item) {} + + /** + * Called when a move animation has ended on the given ViewHolder. + * The default implementation does nothing. Subclasses may wish to override + * this method to handle any ViewHolder-specific operations linked to animation + * lifecycles. + * + * @param item The ViewHolder being animated. + */ + public void onMoveFinished(ViewHolder item) {} + + /** + * Called when a change animation is being started on the given ViewHolder. + * The default implementation does nothing. Subclasses may wish to override + * this method to handle any ViewHolder-specific operations linked to animation + * lifecycles. + * + * @param item The ViewHolder being animated. + * @param oldItem true if this is the old item that was changed, false if + * it is the new item that replaced the old item. + */ + public void onChangeStarting(ViewHolder item, boolean oldItem) {} + + /** + * Called when a change animation has ended on the given ViewHolder. + * The default implementation does nothing. Subclasses may wish to override + * this method to handle any ViewHolder-specific operations linked to animation + * lifecycles. + * + * @param item The ViewHolder being animated. + * @param oldItem true if this is the old item that was changed, false if + * it is the new item that replaced the old item. + */ + public void onChangeFinished(ViewHolder item, boolean oldItem) {} + + } + + /** + * Internal data structure that holds information about an item's bounds. + * This information is used in calculating item animations. + */ + private static class ItemHolderInfo { + ViewHolder holder; + int left, top, right, bottom; + + ItemHolderInfo(ViewHolder holder, int left, int top, int right, int bottom) { + this.holder = holder; + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/RecyclerViewAccessibilityDelegate.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/RecyclerViewAccessibilityDelegate.java new file mode 100644 index 000000000..f1cd3b0b2 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/RecyclerViewAccessibilityDelegate.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.telegram.android.support.widget; + +import android.os.Bundle; +import android.support.v4.view.AccessibilityDelegateCompat; +import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; + +/** + * The AccessibilityDelegate used by RecyclerView. + *

+ * This class handles basic accessibility actions and delegates them to LayoutManager. + */ +public class RecyclerViewAccessibilityDelegate extends AccessibilityDelegateCompat { + final RecyclerView mRecyclerView; + + + public RecyclerViewAccessibilityDelegate(RecyclerView recyclerView) { + mRecyclerView = recyclerView; + } + + private boolean shouldIgnore() { + return mRecyclerView.hasPendingAdapterUpdates(); + } + + @Override + public boolean performAccessibilityAction(View host, int action, Bundle args) { + if (super.performAccessibilityAction(host, action, args)) { + return true; + } + if (!shouldIgnore() && mRecyclerView.getLayoutManager() != null) { + return mRecyclerView.getLayoutManager().performAccessibilityAction(action, args); + } + + return false; + } + + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { + super.onInitializeAccessibilityNodeInfo(host, info); + info.setClassName(RecyclerView.class.getName()); + if (!shouldIgnore() && mRecyclerView.getLayoutManager() != null) { + mRecyclerView.getLayoutManager().onInitializeAccessibilityNodeInfo(info); + } + } + + @Override + public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) { + super.onInitializeAccessibilityEvent(host, event); + event.setClassName(RecyclerView.class.getName()); + if (host instanceof RecyclerView && !shouldIgnore()) { + RecyclerView rv = (RecyclerView) host; + if (rv.getLayoutManager() != null) { + rv.getLayoutManager().onInitializeAccessibilityEvent(event); + } + } + } + + AccessibilityDelegateCompat getItemDelegate() { + return mItemDelegate; + } + + final AccessibilityDelegateCompat mItemDelegate = new AccessibilityDelegateCompat() { + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { + super.onInitializeAccessibilityNodeInfo(host, info); + if (!shouldIgnore() && mRecyclerView.getLayoutManager() != null) { + mRecyclerView.getLayoutManager(). + onInitializeAccessibilityNodeInfoForItem(host, info); + } + } + + @Override + public boolean performAccessibilityAction(View host, int action, Bundle args) { + if (super.performAccessibilityAction(host, action, args)) { + return true; + } + if (!shouldIgnore() && mRecyclerView.getLayoutManager() != null) { + return mRecyclerView.getLayoutManager(). + performAccessibilityActionForItem(host, action, args); + } + return false; + } + }; +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/ScrollbarHelper.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/ScrollbarHelper.java new file mode 100644 index 000000000..9df5bba2f --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/ScrollbarHelper.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.telegram.android.support.widget; + +import android.view.View; + +/** + * A helper class to do scroll offset calculations. + */ +class ScrollbarHelper { + + /** + * @param startChild View closest to start of the list. (top or left) + * @param endChild View closest to end of the list (bottom or right) + */ + static int computeScrollOffset(RecyclerView.State state, OrientationHelper orientation, + View startChild, View endChild, RecyclerView.LayoutManager lm, + boolean smoothScrollbarEnabled, boolean reverseLayout) { + if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null || + endChild == null) { + return 0; + } + final int minPosition = Math.min(lm.getPosition(startChild), + lm.getPosition(endChild)); + final int maxPosition = Math.max(lm.getPosition(startChild), + lm.getPosition(endChild)); + final int itemsBefore = reverseLayout + ? Math.max(0, state.getItemCount() - maxPosition - 1) + : Math.max(0, minPosition); + if (!smoothScrollbarEnabled) { + return itemsBefore; + } + final int laidOutArea = Math.abs(orientation.getDecoratedEnd(endChild) - + orientation.getDecoratedStart(startChild)); + final int itemRange = Math.abs(lm.getPosition(startChild) - + lm.getPosition(endChild)) + 1; + final float avgSizePerRow = (float) laidOutArea / itemRange; + + return Math.round(itemsBefore * avgSizePerRow + (orientation.getStartAfterPadding() + - orientation.getDecoratedStart(startChild))); + } + + /** + * @param startChild View closest to start of the list. (top or left) + * @param endChild View closest to end of the list (bottom or right) + */ + static int computeScrollExtent(RecyclerView.State state, OrientationHelper orientation, + View startChild, View endChild, RecyclerView.LayoutManager lm, + boolean smoothScrollbarEnabled) { + if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null || + endChild == null) { + return 0; + } + if (!smoothScrollbarEnabled) { + return Math.abs(lm.getPosition(startChild) - lm.getPosition(endChild)) + 1; + } + final int extend = orientation.getDecoratedEnd(endChild) + - orientation.getDecoratedStart(startChild); + return Math.min(orientation.getTotalSpace(), extend); + } + + /** + * @param startChild View closest to start of the list. (top or left) + * @param endChild View closest to end of the list (bottom or right) + */ + static int computeScrollRange(RecyclerView.State state, OrientationHelper orientation, + View startChild, View endChild, RecyclerView.LayoutManager lm, + boolean smoothScrollbarEnabled) { + if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null || + endChild == null) { + return 0; + } + if (!smoothScrollbarEnabled) { + return state.getItemCount(); + } + // smooth scrollbar enabled. try to estimate better. + final int laidOutArea = orientation.getDecoratedEnd(endChild) - + orientation.getDecoratedStart(startChild); + final int laidOutRange = Math.abs(lm.getPosition(startChild) - + lm.getPosition(endChild)) + + 1; + // estimate a size for full list. + return (int) ((float) laidOutArea / laidOutRange * state.getItemCount()); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/StaggeredGridLayoutManager.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/StaggeredGridLayoutManager.java new file mode 100644 index 000000000..d28434a60 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/StaggeredGridLayoutManager.java @@ -0,0 +1,2763 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.telegram.android.support.widget; + +import android.content.Context; +import android.graphics.PointF; +import android.graphics.Rect; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.v4.view.ViewCompat; +import android.support.v4.view.accessibility.AccessibilityEventCompat; +import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; +import android.support.v4.view.accessibility.AccessibilityRecordCompat; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.List; + +import static org.telegram.android.support.widget.LayoutState.LAYOUT_START; +import static org.telegram.android.support.widget.LayoutState.LAYOUT_END; +import static org.telegram.android.support.widget.LayoutState.ITEM_DIRECTION_HEAD; +import static org.telegram.android.support.widget.LayoutState.ITEM_DIRECTION_TAIL; +import static org.telegram.android.support.widget.RecyclerView.NO_POSITION; + +/** + * A LayoutManager that lays out children in a staggered grid formation. + * It supports horizontal & vertical layout as well as an ability to layout children in reverse. + *

+ * Staggered grids are likely to have gaps at the edges of the layout. To avoid these gaps, + * StaggeredGridLayoutManager can offset spans independently or move items between spans. You can + * control this behavior via {@link #setGapStrategy(int)}. + */ +public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager { + + public static final String TAG = "StaggeredGridLayoutManager"; + + private static final boolean DEBUG = false; + + public static final int HORIZONTAL = OrientationHelper.HORIZONTAL; + + public static final int VERTICAL = OrientationHelper.VERTICAL; + + /** + * Does not do anything to hide gaps. + */ + public static final int GAP_HANDLING_NONE = 0; + + @Deprecated + public static final int GAP_HANDLING_LAZY = 1; + + /** + * When scroll state is changed to {@link RecyclerView#SCROLL_STATE_IDLE}, StaggeredGrid will + * check if there are gaps in the because of full span items. If it finds, it will re-layout + * and move items to correct positions with animations. + *

+ * For example, if LayoutManager ends up with the following layout due to adapter changes: + *

+     * AAA
+     * _BC
+     * DDD
+     * 
+ *

+ * It will animate to the following state: + *

+     * AAA
+     * BC_
+     * DDD
+     * 
+ */ + public static final int GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS = 2; + + private static final int INVALID_OFFSET = Integer.MIN_VALUE; + + /** + * Number of spans + */ + private int mSpanCount = -1; + + private Span[] mSpans; + + /** + * Primary orientation is the layout's orientation, secondary orientation is the orientation + * for spans. Having both makes code much cleaner for calculations. + */ + OrientationHelper mPrimaryOrientation; + OrientationHelper mSecondaryOrientation; + + private int mOrientation; + + /** + * The width or height per span, depending on the orientation. + */ + private int mSizePerSpan; + + private LayoutState mLayoutState; + + private boolean mReverseLayout = false; + + /** + * Aggregated reverse layout value that takes RTL into account. + */ + boolean mShouldReverseLayout = false; + + /** + * Temporary variable used during fill method to check which spans needs to be filled. + */ + private BitSet mRemainingSpans; + + /** + * When LayoutManager needs to scroll to a position, it sets this variable and requests a + * layout which will check this variable and re-layout accordingly. + */ + int mPendingScrollPosition = NO_POSITION; + + /** + * Used to keep the offset value when {@link #scrollToPositionWithOffset(int, int)} is + * called. + */ + int mPendingScrollPositionOffset = INVALID_OFFSET; + + /** + * Keeps the mapping between the adapter positions and spans. This is necessary to provide + * a consistent experience when user scrolls the list. + */ + LazySpanLookup mLazySpanLookup = new LazySpanLookup(); + + /** + * how we handle gaps in UI. + */ + private int mGapStrategy = GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS; + + /** + * Saved state needs this information to properly layout on restore. + */ + private boolean mLastLayoutFromEnd; + + /** + * Saved state and onLayout needs this information to re-layout properly + */ + private boolean mLastLayoutRTL; + + /** + * SavedState is not handled until a layout happens. This is where we keep it until next + * layout. + */ + private SavedState mPendingSavedState; + + /** + * Re-used measurement specs. updated by onLayout. + */ + private int mFullSizeSpec, mWidthSpec, mHeightSpec; + + /** + * Re-used rectangle to get child decor offsets. + */ + private final Rect mTmpRect = new Rect(); + + /** + * Re-used anchor info. + */ + private final AnchorInfo mAnchorInfo = new AnchorInfo(); + + /** + * If a full span item is invalid / or created in reverse direction; it may create gaps in + * the UI. While laying out, if such case is detected, we set this flag. + *

+ * After scrolling stops, we check this flag and if it is set, re-layout. + */ + private boolean mLaidOutInvalidFullSpan = false; + + /** + * Works the same way as {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)}. + * see {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)} + */ + private boolean mSmoothScrollbarEnabled = true; + + private final Runnable mCheckForGapsRunnable = new Runnable() { + @Override + public void run() { + checkForGaps(); + } + }; + + /** + * Creates a StaggeredGridLayoutManager with given parameters. + * + * @param spanCount If orientation is vertical, spanCount is number of columns. If + * orientation is horizontal, spanCount is number of rows. + * @param orientation {@link #VERTICAL} or {@link #HORIZONTAL} + */ + public StaggeredGridLayoutManager(int spanCount, int orientation) { + mOrientation = orientation; + setSpanCount(spanCount); + } + + /** + * Checks for gaps in the UI that may be caused by adapter changes. + *

+ * When a full span item is laid out in reverse direction, it sets a flag which we check when + * scroll is stopped (or re-layout happens) and re-layout after first valid item. + */ + private boolean checkForGaps() { + if (getChildCount() == 0 || mGapStrategy == GAP_HANDLING_NONE || !isAttachedToWindow()) { + return false; + } + final int minPos, maxPos; + if (mShouldReverseLayout) { + minPos = getLastChildPosition(); + maxPos = getFirstChildPosition(); + } else { + minPos = getFirstChildPosition(); + maxPos = getLastChildPosition(); + } + if (minPos == 0) { + View gapView = hasGapsToFix(); + if (gapView != null) { + mLazySpanLookup.clear(); + requestSimpleAnimationsInNextLayout(); + requestLayout(); + return true; + } + } + if (!mLaidOutInvalidFullSpan) { + return false; + } + int invalidGapDir = mShouldReverseLayout ? LAYOUT_START : LAYOUT_END; + final LazySpanLookup.FullSpanItem invalidFsi = mLazySpanLookup + .getFirstFullSpanItemInRange(minPos, maxPos + 1, invalidGapDir, true); + if (invalidFsi == null) { + mLaidOutInvalidFullSpan = false; + mLazySpanLookup.forceInvalidateAfter(maxPos + 1); + return false; + } + final LazySpanLookup.FullSpanItem validFsi = mLazySpanLookup + .getFirstFullSpanItemInRange(minPos, invalidFsi.mPosition, + invalidGapDir * -1, true); + if (validFsi == null) { + mLazySpanLookup.forceInvalidateAfter(invalidFsi.mPosition); + } else { + mLazySpanLookup.forceInvalidateAfter(validFsi.mPosition + 1); + } + requestSimpleAnimationsInNextLayout(); + requestLayout(); + return true; + } + + @Override + public void onScrollStateChanged(int state) { + if (state == RecyclerView.SCROLL_STATE_IDLE) { + checkForGaps(); + } + } + + @Override + public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) { + removeCallbacks(mCheckForGapsRunnable); + for (int i = 0; i < mSpanCount; i++) { + mSpans[i].clear(); + } + } + + /** + * Checks for gaps if we've reached to the top of the list. + *

+ * Intermediate gaps created by full span items are tracked via mLaidOutInvalidFullSpan field. + */ + View hasGapsToFix() { + int startChildIndex = 0; + int endChildIndex = getChildCount() - 1; + BitSet mSpansToCheck = new BitSet(mSpanCount); + mSpansToCheck.set(0, mSpanCount, true); + + final int firstChildIndex, childLimit; + final int preferredSpanDir = mOrientation == VERTICAL && isLayoutRTL() ? 1 : -1; + + if (mShouldReverseLayout) { + firstChildIndex = endChildIndex; + childLimit = startChildIndex - 1; + } else { + firstChildIndex = startChildIndex; + childLimit = endChildIndex + 1; + } + final int nextChildDiff = firstChildIndex < childLimit ? 1 : -1; + for (int i = firstChildIndex; i != childLimit; i += nextChildDiff) { + View child = getChildAt(i); + LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (mSpansToCheck.get(lp.mSpan.mIndex)) { + if (checkSpanForGap(lp.mSpan)) { + return child; + } + mSpansToCheck.clear(lp.mSpan.mIndex); + } + if (lp.mFullSpan) { + continue; // quick reject + } + + if (i + nextChildDiff != childLimit) { + View nextChild = getChildAt(i + nextChildDiff); + boolean compareSpans = false; + if (mShouldReverseLayout) { + // ensure child's end is below nextChild's end + int myEnd = mPrimaryOrientation.getDecoratedEnd(child); + int nextEnd = mPrimaryOrientation.getDecoratedEnd(nextChild); + if (myEnd < nextEnd) { + return child;//i should have a better position + } else if (myEnd == nextEnd) { + compareSpans = true; + } + } else { + int myStart = mPrimaryOrientation.getDecoratedStart(child); + int nextStart = mPrimaryOrientation.getDecoratedStart(nextChild); + if (myStart > nextStart) { + return child;//i should have a better position + } else if (myStart == nextStart) { + compareSpans = true; + } + } + if (compareSpans) { + // equal, check span indices. + LayoutParams nextLp = (LayoutParams) nextChild.getLayoutParams(); + if (lp.mSpan.mIndex - nextLp.mSpan.mIndex < 0 != preferredSpanDir < 0) { + return child; + } + } + } + } + // everything looks good + return null; + } + + private boolean checkSpanForGap(Span span) { + if (mShouldReverseLayout) { + if (span.getEndLine() < mPrimaryOrientation.getEndAfterPadding()) { + return true; + } + } else if (span.getStartLine() > mPrimaryOrientation.getStartAfterPadding()) { + return true; + } + return false; + } + + /** + * Sets the number of spans for the layout. This will invalidate all of the span assignments + * for Views. + *

+ * Calling this method will automatically result in a new layout request unless the spanCount + * parameter is equal to current span count. + * + * @param spanCount Number of spans to layout + */ + public void setSpanCount(int spanCount) { + assertNotInLayoutOrScroll(null); + if (spanCount != mSpanCount) { + invalidateSpanAssignments(); + mSpanCount = spanCount; + mRemainingSpans = new BitSet(mSpanCount); + mSpans = new Span[mSpanCount]; + for (int i = 0; i < mSpanCount; i++) { + mSpans[i] = new Span(i); + } + requestLayout(); + } + } + + /** + * Sets the orientation of the layout. StaggeredGridLayoutManager will do its best to keep + * scroll position if this method is called after views are laid out. + * + * @param orientation {@link #HORIZONTAL} or {@link #VERTICAL} + */ + public void setOrientation(int orientation) { + if (orientation != HORIZONTAL && orientation != VERTICAL) { + throw new IllegalArgumentException("invalid orientation."); + } + assertNotInLayoutOrScroll(null); + if (orientation == mOrientation) { + return; + } + mOrientation = orientation; + if (mPrimaryOrientation != null && mSecondaryOrientation != null) { + // swap + OrientationHelper tmp = mPrimaryOrientation; + mPrimaryOrientation = mSecondaryOrientation; + mSecondaryOrientation = tmp; + } + requestLayout(); + } + + /** + * Sets whether LayoutManager should start laying out items from the end of the UI. The order + * items are traversed is not affected by this call. + *

+ * For vertical layout, if it is set to true, first item will be at the bottom of + * the list. + *

+ * For horizontal layouts, it depends on the layout direction. + * When set to true, If {@link RecyclerView} is LTR, than it will layout from RTL, if + * {@link RecyclerView}} is RTL, it will layout from LTR. + * + * @param reverseLayout Whether layout should be in reverse or not + */ + public void setReverseLayout(boolean reverseLayout) { + assertNotInLayoutOrScroll(null); + if (mPendingSavedState != null && mPendingSavedState.mReverseLayout != reverseLayout) { + mPendingSavedState.mReverseLayout = reverseLayout; + } + mReverseLayout = reverseLayout; + requestLayout(); + } + + /** + * Returns the current gap handling strategy for StaggeredGridLayoutManager. + *

+ * Staggered grid may have gaps in the layout due to changes in the adapter. To avoid gaps, + * StaggeredGridLayoutManager provides 2 options. Check {@link #GAP_HANDLING_NONE} and + * {@link #GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS} for details. + *

+ * By default, StaggeredGridLayoutManager uses {@link #GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS}. + * + * @return Current gap handling strategy. + * @see #setGapStrategy(int) + * @see #GAP_HANDLING_NONE + * @see #GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS + */ + public int getGapStrategy() { + return mGapStrategy; + } + + /** + * Sets the gap handling strategy for StaggeredGridLayoutManager. If the gapStrategy parameter + * is different than the current strategy, calling this method will trigger a layout request. + * + * @param gapStrategy The new gap handling strategy. Should be + * {@link #GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS} or {@link + * #GAP_HANDLING_NONE}. + * @see #getGapStrategy() + */ + public void setGapStrategy(int gapStrategy) { + assertNotInLayoutOrScroll(null); + if (gapStrategy == mGapStrategy) { + return; + } + if (gapStrategy != GAP_HANDLING_NONE && + gapStrategy != GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS) { + throw new IllegalArgumentException("invalid gap strategy. Must be GAP_HANDLING_NONE " + + "or GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS"); + } + mGapStrategy = gapStrategy; + requestLayout(); + } + + @Override + public void assertNotInLayoutOrScroll(String message) { + if (mPendingSavedState == null) { + super.assertNotInLayoutOrScroll(message); + } + } + + /** + * Returns the number of spans laid out by StaggeredGridLayoutManager. + * + * @return Number of spans in the layout + */ + public int getSpanCount() { + return mSpanCount; + } + + /** + * For consistency, StaggeredGridLayoutManager keeps a mapping between spans and items. + *

+ * If you need to cancel current assignments, you can call this method which will clear all + * assignments and request a new layout. + */ + public void invalidateSpanAssignments() { + mLazySpanLookup.clear(); + requestLayout(); + } + + private void ensureOrientationHelper() { + if (mPrimaryOrientation == null) { + mPrimaryOrientation = OrientationHelper.createOrientationHelper(this, mOrientation); + mSecondaryOrientation = OrientationHelper + .createOrientationHelper(this, 1 - mOrientation); + mLayoutState = new LayoutState(); + } + } + + /** + * Calculates the views' layout order. (e.g. from end to start or start to end) + * RTL layout support is applied automatically. So if layout is RTL and + * {@link #getReverseLayout()} is {@code true}, elements will be laid out starting from left. + */ + private void resolveShouldLayoutReverse() { + // A == B is the same result, but we rather keep it readable + if (mOrientation == VERTICAL || !isLayoutRTL()) { + mShouldReverseLayout = mReverseLayout; + } else { + mShouldReverseLayout = !mReverseLayout; + } + } + + boolean isLayoutRTL() { + return getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL; + } + + /** + * Returns whether views are laid out in reverse order or not. + *

+ * Not that this value is not affected by RecyclerView's layout direction. + * + * @return True if layout is reversed, false otherwise + * @see #setReverseLayout(boolean) + */ + public boolean getReverseLayout() { + return mReverseLayout; + } + @Override + public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { + ensureOrientationHelper(); + + final AnchorInfo anchorInfo = mAnchorInfo; + anchorInfo.reset(); + + if (mPendingSavedState != null) { + applyPendingSavedState(anchorInfo); + } else { + resolveShouldLayoutReverse(); + anchorInfo.mLayoutFromEnd = mShouldReverseLayout; + } + + updateAnchorInfoForLayout(state, anchorInfo); + + if (mPendingSavedState == null) { + if (anchorInfo.mLayoutFromEnd != mLastLayoutFromEnd || + isLayoutRTL() != mLastLayoutRTL) { + mLazySpanLookup.clear(); + anchorInfo.mInvalidateOffsets = true; + } + } + + if (getChildCount() > 0 && (mPendingSavedState == null || + mPendingSavedState.mSpanOffsetsSize < 1)) { + if (anchorInfo.mInvalidateOffsets) { + for (int i = 0; i < mSpanCount; i++) { + // Scroll to position is set, clear. + mSpans[i].clear(); + if (anchorInfo.mOffset != INVALID_OFFSET) { + mSpans[i].setLine(anchorInfo.mOffset); + } + } + } else { + for (int i = 0; i < mSpanCount; i++) { + mSpans[i].cacheReferenceLineAndClear(mShouldReverseLayout, anchorInfo.mOffset); + } + } + } + detachAndScrapAttachedViews(recycler); + mLaidOutInvalidFullSpan = false; + updateMeasureSpecs(); + if (anchorInfo.mLayoutFromEnd) { + // Layout start. + updateLayoutStateToFillStart(anchorInfo.mPosition, state); + fill(recycler, mLayoutState, state); + // Layout end. + updateLayoutStateToFillEnd(anchorInfo.mPosition, state); + mLayoutState.mCurrentPosition += mLayoutState.mItemDirection; + fill(recycler, mLayoutState, state); + } else { + // Layout end. + updateLayoutStateToFillEnd(anchorInfo.mPosition, state); + fill(recycler, mLayoutState, state); + // Layout start. + updateLayoutStateToFillStart(anchorInfo.mPosition, state); + mLayoutState.mCurrentPosition += mLayoutState.mItemDirection; + fill(recycler, mLayoutState, state); + } + + if (getChildCount() > 0) { + if (mShouldReverseLayout) { + fixEndGap(recycler, state, true); + fixStartGap(recycler, state, false); + } else { + fixStartGap(recycler, state, true); + fixEndGap(recycler, state, false); + } + } + + if (!state.isPreLayout()) { + final boolean needToCheckForGaps = mGapStrategy != GAP_HANDLING_NONE + && getChildCount() > 0 + && (mLaidOutInvalidFullSpan || hasGapsToFix() != null); + if (needToCheckForGaps) { + removeCallbacks(mCheckForGapsRunnable); + postOnAnimation(mCheckForGapsRunnable); + } + mPendingScrollPosition = NO_POSITION; + mPendingScrollPositionOffset = INVALID_OFFSET; + } + mLastLayoutFromEnd = anchorInfo.mLayoutFromEnd; + mLastLayoutRTL = isLayoutRTL(); + mPendingSavedState = null; // we don't need this anymore + } + + private void applyPendingSavedState(AnchorInfo anchorInfo) { + if (DEBUG) { + Log.d(TAG, "found saved state: " + mPendingSavedState); + } + if (mPendingSavedState.mSpanOffsetsSize > 0) { + if (mPendingSavedState.mSpanOffsetsSize == mSpanCount) { + for (int i = 0; i < mSpanCount; i++) { + mSpans[i].clear(); + int line = mPendingSavedState.mSpanOffsets[i]; + if (line != Span.INVALID_LINE) { + if (mPendingSavedState.mAnchorLayoutFromEnd) { + line += mPrimaryOrientation.getEndAfterPadding(); + } else { + line += mPrimaryOrientation.getStartAfterPadding(); + } + } + mSpans[i].setLine(line); + } + } else { + mPendingSavedState.invalidateSpanInfo(); + mPendingSavedState.mAnchorPosition = mPendingSavedState.mVisibleAnchorPosition; + } + } + mLastLayoutRTL = mPendingSavedState.mLastLayoutRTL; + setReverseLayout(mPendingSavedState.mReverseLayout); + resolveShouldLayoutReverse(); + + if (mPendingSavedState.mAnchorPosition != NO_POSITION) { + mPendingScrollPosition = mPendingSavedState.mAnchorPosition; + anchorInfo.mLayoutFromEnd = mPendingSavedState.mAnchorLayoutFromEnd; + } else { + anchorInfo.mLayoutFromEnd = mShouldReverseLayout; + } + if (mPendingSavedState.mSpanLookupSize > 1) { + mLazySpanLookup.mData = mPendingSavedState.mSpanLookup; + mLazySpanLookup.mFullSpanItems = mPendingSavedState.mFullSpanItems; + } + } + + void updateAnchorInfoForLayout(RecyclerView.State state, AnchorInfo anchorInfo) { + if (updateAnchorFromPendingData(state, anchorInfo)) { + return; + } + if (updateAnchorFromChildren(state, anchorInfo)) { + return; + } + if (DEBUG) { + Log.d(TAG, "Deciding anchor info from fresh state"); + } + anchorInfo.assignCoordinateFromPadding(); + anchorInfo.mPosition = 0; + } + + private boolean updateAnchorFromChildren(RecyclerView.State state, AnchorInfo anchorInfo) { + // We don't recycle views out of adapter order. This way, we can rely on the first or + // last child as the anchor position. + // Layout direction may change but we should select the child depending on the latest + // layout direction. Otherwise, we'll choose the wrong child. + anchorInfo.mPosition = mLastLayoutFromEnd + ? findLastReferenceChildPosition(state.getItemCount()) + : findFirstReferenceChildPosition(state.getItemCount()); + anchorInfo.mOffset = INVALID_OFFSET; + return true; + } + + boolean updateAnchorFromPendingData(RecyclerView.State state, AnchorInfo anchorInfo) { + // Validate scroll position if exists. + if (state.isPreLayout() || mPendingScrollPosition == NO_POSITION) { + return false; + } + // Validate it. + if (mPendingScrollPosition < 0 || mPendingScrollPosition >= state.getItemCount()) { + mPendingScrollPosition = NO_POSITION; + mPendingScrollPositionOffset = INVALID_OFFSET; + return false; + } + + if (mPendingSavedState == null || mPendingSavedState.mAnchorPosition == NO_POSITION + || mPendingSavedState.mSpanOffsetsSize < 1) { + // If item is visible, make it fully visible. + final View child = findViewByPosition(mPendingScrollPosition); + if (child != null) { + // Use regular anchor position, offset according to pending offset and target + // child + anchorInfo.mPosition = mShouldReverseLayout ? getLastChildPosition() + : getFirstChildPosition(); + + if (mPendingScrollPositionOffset != INVALID_OFFSET) { + if (anchorInfo.mLayoutFromEnd) { + final int target = mPrimaryOrientation.getEndAfterPadding() - + mPendingScrollPositionOffset; + anchorInfo.mOffset = target - mPrimaryOrientation.getDecoratedEnd(child); + } else { + final int target = mPrimaryOrientation.getStartAfterPadding() + + mPendingScrollPositionOffset; + anchorInfo.mOffset = target - mPrimaryOrientation.getDecoratedStart(child); + } + return true; + } + + // no offset provided. Decide according to the child location + final int childSize = mPrimaryOrientation.getDecoratedMeasurement(child); + if (childSize > mPrimaryOrientation.getTotalSpace()) { + // Item does not fit. Fix depending on layout direction. + anchorInfo.mOffset = anchorInfo.mLayoutFromEnd + ? mPrimaryOrientation.getEndAfterPadding() + : mPrimaryOrientation.getStartAfterPadding(); + return true; + } + + final int startGap = mPrimaryOrientation.getDecoratedStart(child) + - mPrimaryOrientation.getStartAfterPadding(); + if (startGap < 0) { + anchorInfo.mOffset = -startGap; + return true; + } + final int endGap = mPrimaryOrientation.getEndAfterPadding() - + mPrimaryOrientation.getDecoratedEnd(child); + if (endGap < 0) { + anchorInfo.mOffset = endGap; + return true; + } + // child already visible. just layout as usual + anchorInfo.mOffset = INVALID_OFFSET; + } else { + // Child is not visible. Set anchor coordinate depending on in which direction + // child will be visible. + anchorInfo.mPosition = mPendingScrollPosition; + if (mPendingScrollPositionOffset == INVALID_OFFSET) { + final int position = calculateScrollDirectionForPosition( + anchorInfo.mPosition); + anchorInfo.mLayoutFromEnd = position == LAYOUT_END; + anchorInfo.assignCoordinateFromPadding(); + } else { + anchorInfo.assignCoordinateFromPadding(mPendingScrollPositionOffset); + } + anchorInfo.mInvalidateOffsets = true; + } + } else { + anchorInfo.mOffset = INVALID_OFFSET; + anchorInfo.mPosition = mPendingScrollPosition; + } + return true; + } + + void updateMeasureSpecs() { + mSizePerSpan = mSecondaryOrientation.getTotalSpace() / mSpanCount; + mFullSizeSpec = View.MeasureSpec.makeMeasureSpec( + mSecondaryOrientation.getTotalSpace(), View.MeasureSpec.EXACTLY); + if (mOrientation == VERTICAL) { + mWidthSpec = View.MeasureSpec.makeMeasureSpec(mSizePerSpan, View.MeasureSpec.EXACTLY); + mHeightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); + } else { + mHeightSpec = View.MeasureSpec.makeMeasureSpec(mSizePerSpan, View.MeasureSpec.EXACTLY); + mWidthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); + } + } + + @Override + public boolean supportsPredictiveItemAnimations() { + return mPendingSavedState == null; + } + + /** + * Returns the adapter position of the first visible view for each span. + *

+ * Note that, this value is not affected by layout orientation or item order traversal. + * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter, + * not in the layout. + *

+ * If RecyclerView has item decorators, they will be considered in calculations as well. + *

+ * StaggeredGridLayoutManager may pre-cache some views that are not necessarily visible. Those + * views are ignored in this method. + * + * @param into An array to put the results into. If you don't provide any, LayoutManager will + * create a new one. + * @return The adapter position of the first visible item in each span. If a span does not have + * any items, {@link RecyclerView#NO_POSITION} is returned for that span. + * @see #findFirstCompletelyVisibleItemPositions(int[]) + * @see #findLastVisibleItemPositions(int[]) + */ + public int[] findFirstVisibleItemPositions(int[] into) { + if (into == null) { + into = new int[mSpanCount]; + } else if (into.length < mSpanCount) { + throw new IllegalArgumentException("Provided int[]'s size must be more than or equal" + + " to span count. Expected:" + mSpanCount + ", array size:" + into.length); + } + for (int i = 0; i < mSpanCount; i++) { + into[i] = mSpans[i].findFirstVisibleItemPosition(); + } + return into; + } + + /** + * Returns the adapter position of the first completely visible view for each span. + *

+ * Note that, this value is not affected by layout orientation or item order traversal. + * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter, + * not in the layout. + *

+ * If RecyclerView has item decorators, they will be considered in calculations as well. + *

+ * StaggeredGridLayoutManager may pre-cache some views that are not necessarily visible. Those + * views are ignored in this method. + * + * @param into An array to put the results into. If you don't provide any, LayoutManager will + * create a new one. + * @return The adapter position of the first fully visible item in each span. If a span does + * not have any items, {@link RecyclerView#NO_POSITION} is returned for that span. + * @see #findFirstVisibleItemPositions(int[]) + * @see #findLastCompletelyVisibleItemPositions(int[]) + */ + public int[] findFirstCompletelyVisibleItemPositions(int[] into) { + if (into == null) { + into = new int[mSpanCount]; + } else if (into.length < mSpanCount) { + throw new IllegalArgumentException("Provided int[]'s size must be more than or equal" + + " to span count. Expected:" + mSpanCount + ", array size:" + into.length); + } + for (int i = 0; i < mSpanCount; i++) { + into[i] = mSpans[i].findFirstCompletelyVisibleItemPosition(); + } + return into; + } + + /** + * Returns the adapter position of the last visible view for each span. + *

+ * Note that, this value is not affected by layout orientation or item order traversal. + * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter, + * not in the layout. + *

+ * If RecyclerView has item decorators, they will be considered in calculations as well. + *

+ * StaggeredGridLayoutManager may pre-cache some views that are not necessarily visible. Those + * views are ignored in this method. + * + * @param into An array to put the results into. If you don't provide any, LayoutManager will + * create a new one. + * @return The adapter position of the last visible item in each span. If a span does not have + * any items, {@link RecyclerView#NO_POSITION} is returned for that span. + * @see #findLastCompletelyVisibleItemPositions(int[]) + * @see #findFirstVisibleItemPositions(int[]) + */ + public int[] findLastVisibleItemPositions(int[] into) { + if (into == null) { + into = new int[mSpanCount]; + } else if (into.length < mSpanCount) { + throw new IllegalArgumentException("Provided int[]'s size must be more than or equal" + + " to span count. Expected:" + mSpanCount + ", array size:" + into.length); + } + for (int i = 0; i < mSpanCount; i++) { + into[i] = mSpans[i].findLastVisibleItemPosition(); + } + return into; + } + + /** + * Returns the adapter position of the last completely visible view for each span. + *

+ * Note that, this value is not affected by layout orientation or item order traversal. + * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter, + * not in the layout. + *

+ * If RecyclerView has item decorators, they will be considered in calculations as well. + *

+ * StaggeredGridLayoutManager may pre-cache some views that are not necessarily visible. Those + * views are ignored in this method. + * + * @param into An array to put the results into. If you don't provide any, LayoutManager will + * create a new one. + * @return The adapter position of the last fully visible item in each span. If a span does not + * have any items, {@link RecyclerView#NO_POSITION} is returned for that span. + * @see #findFirstCompletelyVisibleItemPositions(int[]) + * @see #findLastVisibleItemPositions(int[]) + */ + public int[] findLastCompletelyVisibleItemPositions(int[] into) { + if (into == null) { + into = new int[mSpanCount]; + } else if (into.length < mSpanCount) { + throw new IllegalArgumentException("Provided int[]'s size must be more than or equal" + + " to span count. Expected:" + mSpanCount + ", array size:" + into.length); + } + for (int i = 0; i < mSpanCount; i++) { + into[i] = mSpans[i].findLastCompletelyVisibleItemPosition(); + } + return into; + } + + @Override + public int computeHorizontalScrollOffset(RecyclerView.State state) { + return computeScrollOffset(state); + } + + private int computeScrollOffset(RecyclerView.State state) { + if (getChildCount() == 0) { + return 0; + } + ensureOrientationHelper(); + return ScrollbarHelper.computeScrollOffset(state, mPrimaryOrientation, + findFirstVisibleItemClosestToStart(!mSmoothScrollbarEnabled, true) + , findFirstVisibleItemClosestToEnd(!mSmoothScrollbarEnabled, true), + this, mSmoothScrollbarEnabled, mShouldReverseLayout); + } + + @Override + public int computeVerticalScrollOffset(RecyclerView.State state) { + return computeScrollOffset(state); + } + + @Override + public int computeHorizontalScrollExtent(RecyclerView.State state) { + return computeScrollExtent(state); + } + + private int computeScrollExtent(RecyclerView.State state) { + if (getChildCount() == 0) { + return 0; + } + ensureOrientationHelper(); + return ScrollbarHelper.computeScrollExtent(state, mPrimaryOrientation, + findFirstVisibleItemClosestToStart(!mSmoothScrollbarEnabled, true) + , findFirstVisibleItemClosestToEnd(!mSmoothScrollbarEnabled, true), + this, mSmoothScrollbarEnabled); + } + + @Override + public int computeVerticalScrollExtent(RecyclerView.State state) { + return computeScrollExtent(state); + } + + @Override + public int computeHorizontalScrollRange(RecyclerView.State state) { + return computeScrollRange(state); + } + + private int computeScrollRange(RecyclerView.State state) { + if (getChildCount() == 0) { + return 0; + } + ensureOrientationHelper(); + return ScrollbarHelper.computeScrollRange(state, mPrimaryOrientation, + findFirstVisibleItemClosestToStart(!mSmoothScrollbarEnabled, true) + , findFirstVisibleItemClosestToEnd(!mSmoothScrollbarEnabled, true), + this, mSmoothScrollbarEnabled); + } + + @Override + public int computeVerticalScrollRange(RecyclerView.State state) { + return computeScrollRange(state); + } + + private void measureChildWithDecorationsAndMargin(View child, LayoutParams lp) { + if (lp.mFullSpan) { + if (mOrientation == VERTICAL) { + measureChildWithDecorationsAndMargin(child, mFullSizeSpec, + getSpecForDimension(lp.height, mHeightSpec)); + } else { + measureChildWithDecorationsAndMargin(child, + getSpecForDimension(lp.width, mWidthSpec), mFullSizeSpec); + } + } else { + if (mOrientation == VERTICAL) { + measureChildWithDecorationsAndMargin(child, mWidthSpec, + getSpecForDimension(lp.height, mHeightSpec)); + } else { + measureChildWithDecorationsAndMargin(child, + getSpecForDimension(lp.width, mWidthSpec), mHeightSpec); + } + } + } + + private int getSpecForDimension(int dim, int defaultSpec) { + if (dim < 0) { + return defaultSpec; + } else { + return View.MeasureSpec.makeMeasureSpec(dim, View.MeasureSpec.EXACTLY); + } + } + + private void measureChildWithDecorationsAndMargin(View child, int widthSpec, + int heightSpec) { + calculateItemDecorationsForChild(child, mTmpRect); + LayoutParams lp = (LayoutParams) child.getLayoutParams(); + widthSpec = updateSpecWithExtra(widthSpec, lp.leftMargin + mTmpRect.left, + lp.rightMargin + mTmpRect.right); + heightSpec = updateSpecWithExtra(heightSpec, lp.topMargin + mTmpRect.top, + lp.bottomMargin + mTmpRect.bottom); + child.measure(widthSpec, heightSpec); + } + + private int updateSpecWithExtra(int spec, int startInset, int endInset) { + if (startInset == 0 && endInset == 0) { + return spec; + } + final int mode = View.MeasureSpec.getMode(spec); + if (mode == View.MeasureSpec.AT_MOST || mode == View.MeasureSpec.EXACTLY) { + return View.MeasureSpec.makeMeasureSpec( + View.MeasureSpec.getSize(spec) - startInset - endInset, mode); + } + return spec; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + if (state instanceof SavedState) { + mPendingSavedState = (SavedState) state; + requestLayout(); + } else if (DEBUG) { + Log.d(TAG, "invalid saved state class"); + } + } + + @Override + public Parcelable onSaveInstanceState() { + if (mPendingSavedState != null) { + return new SavedState(mPendingSavedState); + } + SavedState state = new SavedState(); + state.mReverseLayout = mReverseLayout; + state.mAnchorLayoutFromEnd = mLastLayoutFromEnd; + state.mLastLayoutRTL = mLastLayoutRTL; + + if (mLazySpanLookup != null && mLazySpanLookup.mData != null) { + state.mSpanLookup = mLazySpanLookup.mData; + state.mSpanLookupSize = state.mSpanLookup.length; + state.mFullSpanItems = mLazySpanLookup.mFullSpanItems; + } else { + state.mSpanLookupSize = 0; + } + + if (getChildCount() > 0) { + ensureOrientationHelper(); + state.mAnchorPosition = mLastLayoutFromEnd ? getLastChildPosition() + : getFirstChildPosition(); + state.mVisibleAnchorPosition = findFirstVisibleItemPositionInt(); + state.mSpanOffsetsSize = mSpanCount; + state.mSpanOffsets = new int[mSpanCount]; + for (int i = 0; i < mSpanCount; i++) { + int line; + if (mLastLayoutFromEnd) { + line = mSpans[i].getEndLine(Span.INVALID_LINE); + if (line != Span.INVALID_LINE) { + line -= mPrimaryOrientation.getEndAfterPadding(); + } + } else { + line = mSpans[i].getStartLine(Span.INVALID_LINE); + if (line != Span.INVALID_LINE) { + line -= mPrimaryOrientation.getStartAfterPadding(); + } + } + state.mSpanOffsets[i] = line; + } + } else { + state.mAnchorPosition = NO_POSITION; + state.mVisibleAnchorPosition = NO_POSITION; + state.mSpanOffsetsSize = 0; + } + if (DEBUG) { + Log.d(TAG, "saved state:\n" + state); + } + return state; + } + + @Override + public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler, + RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) { + ViewGroup.LayoutParams lp = host.getLayoutParams(); + if (!(lp instanceof LayoutParams)) { + super.onInitializeAccessibilityNodeInfoForItem(host, info); + return; + } + LayoutParams sglp = (LayoutParams) lp; + if (mOrientation == HORIZONTAL) { + info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain( + sglp.getSpanIndex(), sglp.mFullSpan ? mSpanCount : 1, + -1, -1, + sglp.mFullSpan, false)); + } else { // VERTICAL + info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain( + -1, -1, + sglp.getSpanIndex(), sglp.mFullSpan ? mSpanCount : 1, + sglp.mFullSpan, false)); + } + } + + @Override + public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + super.onInitializeAccessibilityEvent(event); + if (getChildCount() > 0) { + final AccessibilityRecordCompat record = AccessibilityEventCompat + .asRecord(event); + final View start = findFirstVisibleItemClosestToStart(false, true); + final View end = findFirstVisibleItemClosestToEnd(false, true); + if (start == null || end == null) { + return; + } + final int startPos = getPosition(start); + final int endPos = getPosition(end); + if (startPos < endPos) { + record.setFromIndex(startPos); + record.setToIndex(endPos); + } else { + record.setFromIndex(endPos); + record.setToIndex(startPos); + } + } + } + + /** + * Finds the first fully visible child to be used as an anchor child if span count changes when + * state is restored. If no children is fully visible, returns a partially visible child instead + * of returning null. + */ + int findFirstVisibleItemPositionInt() { + final View first = mShouldReverseLayout ? findFirstVisibleItemClosestToEnd(true, true) : + findFirstVisibleItemClosestToStart(true, true); + return first == null ? NO_POSITION : getPosition(first); + } + + @Override + public int getRowCountForAccessibility(RecyclerView.Recycler recycler, + RecyclerView.State state) { + if (mOrientation == HORIZONTAL) { + return mSpanCount; + } + return super.getRowCountForAccessibility(recycler, state); + } + + @Override + public int getColumnCountForAccessibility(RecyclerView.Recycler recycler, + RecyclerView.State state) { + if (mOrientation == VERTICAL) { + return mSpanCount; + } + return super.getColumnCountForAccessibility(recycler, state); + } + + /** + * This is for internal use. Not necessarily the child closest to start but the first child + * we find that matches the criteria. + * This method does not do any sorting based on child's start coordinate, instead, it uses + * children order. + */ + View findFirstVisibleItemClosestToStart(boolean fullyVisible, boolean acceptPartiallyVisible) { + ensureOrientationHelper(); + final int boundsStart = mPrimaryOrientation.getStartAfterPadding(); + final int boundsEnd = mPrimaryOrientation.getEndAfterPadding(); + final int limit = getChildCount(); + View partiallyVisible = null; + for (int i = 0; i < limit; i++) { + final View child = getChildAt(i); + final int childStart = mPrimaryOrientation.getDecoratedStart(child); + final int childEnd = mPrimaryOrientation.getDecoratedEnd(child); + if(childEnd <= boundsStart || childStart >= boundsEnd) { + continue; // not visible at all + } + if (childStart >= boundsStart || !fullyVisible) { + // when checking for start, it is enough even if part of the child's top is visible + // as long as fully visible is not requested. + return child; + } + if (acceptPartiallyVisible && partiallyVisible == null) { + partiallyVisible = child; + } + } + return partiallyVisible; + } + + /** + * This is for internal use. Not necessarily the child closest to bottom but the first child + * we find that matches the criteria. + * This method does not do any sorting based on child's end coordinate, instead, it uses + * children order. + */ + View findFirstVisibleItemClosestToEnd(boolean fullyVisible, boolean acceptPartiallyVisible) { + ensureOrientationHelper(); + final int boundsStart = mPrimaryOrientation.getStartAfterPadding(); + final int boundsEnd = mPrimaryOrientation.getEndAfterPadding(); + View partiallyVisible = null; + for (int i = getChildCount() - 1; i >= 0; i--) { + final View child = getChildAt(i); + final int childStart = mPrimaryOrientation.getDecoratedStart(child); + final int childEnd = mPrimaryOrientation.getDecoratedEnd(child); + if(childEnd <= boundsStart || childStart >= boundsEnd) { + continue; // not visible at all + } + if (childEnd <= boundsEnd || !fullyVisible) { + // when checking for end, it is enough even if part of the child's bottom is visible + // as long as fully visible is not requested. + return child; + } + if (acceptPartiallyVisible && partiallyVisible == null) { + partiallyVisible = child; + } + } + return partiallyVisible; + } + + private void fixEndGap(RecyclerView.Recycler recycler, RecyclerView.State state, + boolean canOffsetChildren) { + final int maxEndLine = getMaxEnd(mPrimaryOrientation.getEndAfterPadding()); + int gap = mPrimaryOrientation.getEndAfterPadding() - maxEndLine; + int fixOffset; + if (gap > 0) { + fixOffset = -scrollBy(-gap, recycler, state); + } else { + return; // nothing to fix + } + gap -= fixOffset; + if (canOffsetChildren && gap > 0) { + mPrimaryOrientation.offsetChildren(gap); + } + } + + private void fixStartGap(RecyclerView.Recycler recycler, RecyclerView.State state, + boolean canOffsetChildren) { + final int minStartLine = getMinStart(mPrimaryOrientation.getStartAfterPadding()); + int gap = minStartLine - mPrimaryOrientation.getStartAfterPadding(); + int fixOffset; + if (gap > 0) { + fixOffset = scrollBy(gap, recycler, state); + } else { + return; // nothing to fix + } + gap -= fixOffset; + if (canOffsetChildren && gap > 0) { + mPrimaryOrientation.offsetChildren(-gap); + } + } + + private void updateLayoutStateToFillStart(int anchorPosition, RecyclerView.State state) { + mLayoutState.mAvailable = 0; + mLayoutState.mCurrentPosition = anchorPosition; + if (isSmoothScrolling()) { + final int targetPos = state.getTargetScrollPosition(); + if (mShouldReverseLayout == targetPos < anchorPosition) { + mLayoutState.mExtra = 0; + } else { + mLayoutState.mExtra = mPrimaryOrientation.getTotalSpace(); + } + } else { + mLayoutState.mExtra = 0; + } + mLayoutState.mLayoutDirection = LAYOUT_START; + mLayoutState.mItemDirection = mShouldReverseLayout ? ITEM_DIRECTION_TAIL + : ITEM_DIRECTION_HEAD; + } + + private void updateLayoutStateToFillEnd(int anchorPosition, RecyclerView.State state) { + mLayoutState.mAvailable = 0; + mLayoutState.mCurrentPosition = anchorPosition; + if (isSmoothScrolling()) { + final int targetPos = state.getTargetScrollPosition(); + if (mShouldReverseLayout == targetPos > anchorPosition) { + mLayoutState.mExtra = 0; + } else { + mLayoutState.mExtra = mPrimaryOrientation.getTotalSpace(); + } + } else { + mLayoutState.mExtra = 0; + } + mLayoutState.mLayoutDirection = LAYOUT_END; + mLayoutState.mItemDirection = mShouldReverseLayout ? ITEM_DIRECTION_HEAD + : ITEM_DIRECTION_TAIL; + } + + @Override + public void offsetChildrenHorizontal(int dx) { + super.offsetChildrenHorizontal(dx); + for (int i = 0; i < mSpanCount; i++) { + mSpans[i].onOffset(dx); + } + } + + @Override + public void offsetChildrenVertical(int dy) { + super.offsetChildrenVertical(dy); + for (int i = 0; i < mSpanCount; i++) { + mSpans[i].onOffset(dy); + } + } + + @Override + public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) { + handleUpdate(positionStart, itemCount, AdapterHelper.UpdateOp.REMOVE); + } + + @Override + public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) { + handleUpdate(positionStart, itemCount, AdapterHelper.UpdateOp.ADD); + } + + @Override + public void onItemsChanged(RecyclerView recyclerView) { + mLazySpanLookup.clear(); + requestLayout(); + } + + @Override + public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) { + handleUpdate(from, to, AdapterHelper.UpdateOp.MOVE); + } + + @Override + public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) { + handleUpdate(positionStart, itemCount, AdapterHelper.UpdateOp.UPDATE); + } + + /** + * Checks whether it should invalidate span assignments in response to an adapter change. + */ + private void handleUpdate(int positionStart, int itemCountOrToPosition, int cmd) { + int minPosition = mShouldReverseLayout ? getLastChildPosition() : getFirstChildPosition(); + final int affectedRangeEnd;// exclusive + final int affectedRangeStart;// inclusive + + if (cmd == AdapterHelper.UpdateOp.MOVE) { + if (positionStart < itemCountOrToPosition) { + affectedRangeEnd = itemCountOrToPosition + 1; + affectedRangeStart = positionStart; + } else { + affectedRangeEnd = positionStart + 1; + affectedRangeStart = itemCountOrToPosition; + } + } else { + affectedRangeStart = positionStart; + affectedRangeEnd = positionStart + itemCountOrToPosition; + } + + mLazySpanLookup.invalidateAfter(affectedRangeStart); + switch (cmd) { + case AdapterHelper.UpdateOp.ADD: + mLazySpanLookup.offsetForAddition(positionStart, itemCountOrToPosition); + break; + case AdapterHelper.UpdateOp.REMOVE: + mLazySpanLookup.offsetForRemoval(positionStart, itemCountOrToPosition); + break; + case AdapterHelper.UpdateOp.MOVE: + // TODO optimize + mLazySpanLookup.offsetForRemoval(positionStart, 1); + mLazySpanLookup.offsetForAddition(itemCountOrToPosition, 1); + break; + } + + if (affectedRangeEnd <= minPosition) { + return; + } + + int maxPosition = mShouldReverseLayout ? getFirstChildPosition() : getLastChildPosition(); + if (affectedRangeStart <= maxPosition) { + requestLayout(); + } + } + + private int fill(RecyclerView.Recycler recycler, LayoutState layoutState, + RecyclerView.State state) { + mRemainingSpans.set(0, mSpanCount, true); + // The target position we are trying to reach. + final int targetLine; + /* + * The line until which we can recycle, as long as we add views. + * Keep in mind, it is still the line in layout direction which means; to calculate the + * actual recycle line, we should subtract/add the size in orientation. + */ + final int recycleLine; + // Line of the furthest row. + if (layoutState.mLayoutDirection == LAYOUT_END) { + // ignore padding for recycler + recycleLine = mPrimaryOrientation.getEndAfterPadding() + mLayoutState.mAvailable; + targetLine = recycleLine + mLayoutState.mExtra + mPrimaryOrientation.getEndPadding(); + + } else { // LAYOUT_START + // ignore padding for recycler + recycleLine = mPrimaryOrientation.getStartAfterPadding() - mLayoutState.mAvailable; + targetLine = recycleLine - mLayoutState.mExtra - + mPrimaryOrientation.getStartAfterPadding(); + } + updateAllRemainingSpans(layoutState.mLayoutDirection, targetLine); + + // the default coordinate to add new view. + final int defaultNewViewLine = mShouldReverseLayout + ? mPrimaryOrientation.getEndAfterPadding() + : mPrimaryOrientation.getStartAfterPadding(); + + while (layoutState.hasMore(state) && !mRemainingSpans.isEmpty()) { + View view = layoutState.next(recycler); + LayoutParams lp = ((LayoutParams) view.getLayoutParams()); + final int position = lp.getViewLayoutPosition(); + final int spanIndex = mLazySpanLookup.getSpan(position); + Span currentSpan; + final boolean assignSpan = spanIndex == LayoutParams.INVALID_SPAN_ID; + if (assignSpan) { + currentSpan = lp.mFullSpan ? mSpans[0] : getNextSpan(layoutState); + mLazySpanLookup.setSpan(position, currentSpan); + if (DEBUG) { + Log.d(TAG, "assigned " + currentSpan.mIndex + " for " + position); + } + } else { + if (DEBUG) { + Log.d(TAG, "using " + spanIndex + " for pos " + position); + } + currentSpan = mSpans[spanIndex]; + } + // assign span before measuring so that item decorators can get updated span index + lp.mSpan = currentSpan; + if (layoutState.mLayoutDirection == LAYOUT_END) { + addView(view); + } else { + addView(view, 0); + } + measureChildWithDecorationsAndMargin(view, lp); + + final int start; + final int end; + if (layoutState.mLayoutDirection == LAYOUT_END) { + start = lp.mFullSpan ? getMaxEnd(defaultNewViewLine) + : currentSpan.getEndLine(defaultNewViewLine); + end = start + mPrimaryOrientation.getDecoratedMeasurement(view); + if (assignSpan && lp.mFullSpan) { + LazySpanLookup.FullSpanItem fullSpanItem; + fullSpanItem = createFullSpanItemFromEnd(start); + fullSpanItem.mGapDir = LAYOUT_START; + fullSpanItem.mPosition = position; + mLazySpanLookup.addFullSpanItem(fullSpanItem); + } + } else { + end = lp.mFullSpan ? getMinStart(defaultNewViewLine) + : currentSpan.getStartLine(defaultNewViewLine); + start = end - mPrimaryOrientation.getDecoratedMeasurement(view); + if (assignSpan && lp.mFullSpan) { + LazySpanLookup.FullSpanItem fullSpanItem; + fullSpanItem = createFullSpanItemFromStart(end); + fullSpanItem.mGapDir = LAYOUT_END; + fullSpanItem.mPosition = position; + mLazySpanLookup.addFullSpanItem(fullSpanItem); + } + } + + // check if this item may create gaps in the future + if (lp.mFullSpan && layoutState.mItemDirection == ITEM_DIRECTION_HEAD) { + if (assignSpan) { + mLaidOutInvalidFullSpan = true; + } else { + final boolean hasInvalidGap; + if (layoutState.mLayoutDirection == LAYOUT_END) { + hasInvalidGap = !areAllEndsEqual(); + } else { // layoutState.mLayoutDirection == LAYOUT_START + hasInvalidGap = !areAllStartsEqual(); + } + if (hasInvalidGap) { + final LazySpanLookup.FullSpanItem fullSpanItem = mLazySpanLookup + .getFullSpanItem(position); + if (fullSpanItem != null) { + fullSpanItem.mHasUnwantedGapAfter = true; + } + mLaidOutInvalidFullSpan = true; + } + } + + } + attachViewToSpans(view, lp, layoutState); + final int otherStart = lp.mFullSpan ? mSecondaryOrientation.getStartAfterPadding() + : currentSpan.mIndex * mSizePerSpan + + mSecondaryOrientation.getStartAfterPadding(); + final int otherEnd = otherStart + mSecondaryOrientation.getDecoratedMeasurement(view); + if (mOrientation == VERTICAL) { + layoutDecoratedWithMargins(view, otherStart, start, otherEnd, end); + } else { + layoutDecoratedWithMargins(view, start, otherStart, end, otherEnd); + } + + if (lp.mFullSpan) { + updateAllRemainingSpans(mLayoutState.mLayoutDirection, targetLine); + } else { + updateRemainingSpans(currentSpan, mLayoutState.mLayoutDirection, targetLine); + } + recycle(recycler, mLayoutState, currentSpan, recycleLine); + } + if (DEBUG) { + Log.d(TAG, "fill, " + getChildCount()); + } + if (mLayoutState.mLayoutDirection == LAYOUT_START) { + final int minStart = getMinStart(mPrimaryOrientation.getStartAfterPadding()); + return Math.max(0, mLayoutState.mAvailable + (recycleLine - minStart)); + } else { + final int max = getMaxEnd(mPrimaryOrientation.getEndAfterPadding()); + return Math.max(0, mLayoutState.mAvailable + (max - recycleLine)); + } + } + + private LazySpanLookup.FullSpanItem createFullSpanItemFromEnd(int newItemTop) { + LazySpanLookup.FullSpanItem fsi = new LazySpanLookup.FullSpanItem(); + fsi.mGapPerSpan = new int[mSpanCount]; + for (int i = 0; i < mSpanCount; i++) { + fsi.mGapPerSpan[i] = newItemTop - mSpans[i].getEndLine(newItemTop); + } + return fsi; + } + + private LazySpanLookup.FullSpanItem createFullSpanItemFromStart(int newItemBottom) { + LazySpanLookup.FullSpanItem fsi = new LazySpanLookup.FullSpanItem(); + fsi.mGapPerSpan = new int[mSpanCount]; + for (int i = 0; i < mSpanCount; i++) { + fsi.mGapPerSpan[i] = mSpans[i].getStartLine(newItemBottom) - newItemBottom; + } + return fsi; + } + + private void attachViewToSpans(View view, LayoutParams lp, LayoutState layoutState) { + if (layoutState.mLayoutDirection == LayoutState.LAYOUT_END) { + if (lp.mFullSpan) { + appendViewToAllSpans(view); + } else { + lp.mSpan.appendToSpan(view); + } + } else { + if (lp.mFullSpan) { + prependViewToAllSpans(view); + } else { + lp.mSpan.prependToSpan(view); + } + } + } + + private void recycle(RecyclerView.Recycler recycler, LayoutState layoutState, + Span updatedSpan, int recycleLine) { + if (layoutState.mLayoutDirection == LAYOUT_START) { + // calculate recycle line + int maxStart = getMaxStart(updatedSpan.getStartLine()); + recycleFromEnd(recycler, Math.max(recycleLine, maxStart) + + (mPrimaryOrientation.getEnd() - mPrimaryOrientation.getStartAfterPadding())); + } else { + // calculate recycle line + int minEnd = getMinEnd(updatedSpan.getEndLine()); + recycleFromStart(recycler, Math.min(recycleLine, minEnd) - + (mPrimaryOrientation.getEnd() - mPrimaryOrientation.getStartAfterPadding())); + } + } + + private void appendViewToAllSpans(View view) { + // traverse in reverse so that we end up assigning full span items to 0 + for (int i = mSpanCount - 1; i >= 0; i--) { + mSpans[i].appendToSpan(view); + } + } + + private void prependViewToAllSpans(View view) { + // traverse in reverse so that we end up assigning full span items to 0 + for (int i = mSpanCount - 1; i >= 0; i--) { + mSpans[i].prependToSpan(view); + } + } + + private void layoutDecoratedWithMargins(View child, int left, int top, int right, int bottom) { + LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (DEBUG) { + Log.d(TAG, "layout decorated pos: " + lp.getViewLayoutPosition() + ", span:" + + lp.getSpanIndex() + ", fullspan:" + lp.mFullSpan + + ". l:" + left + ",t:" + top + + ", r:" + right + ", b:" + bottom); + } + layoutDecorated(child, left + lp.leftMargin, top + lp.topMargin, right - lp.rightMargin + , bottom - lp.bottomMargin); + } + + private void updateAllRemainingSpans(int layoutDir, int targetLine) { + for (int i = 0; i < mSpanCount; i++) { + if (mSpans[i].mViews.isEmpty()) { + continue; + } + updateRemainingSpans(mSpans[i], layoutDir, targetLine); + } + } + + private void updateRemainingSpans(Span span, int layoutDir, int targetLine) { + final int deletedSize = span.getDeletedSize(); + if (layoutDir == LAYOUT_START) { + final int line = span.getStartLine(); + if (line + deletedSize < targetLine) { + mRemainingSpans.set(span.mIndex, false); + } + } else { + final int line = span.getEndLine(); + if (line - deletedSize > targetLine) { + mRemainingSpans.set(span.mIndex, false); + } + } + } + + private int getMaxStart(int def) { + int maxStart = mSpans[0].getStartLine(def); + for (int i = 1; i < mSpanCount; i++) { + final int spanStart = mSpans[i].getStartLine(def); + if (spanStart > maxStart) { + maxStart = spanStart; + } + } + return maxStart; + } + + private int getMinStart(int def) { + int minStart = mSpans[0].getStartLine(def); + for (int i = 1; i < mSpanCount; i++) { + final int spanStart = mSpans[i].getStartLine(def); + if (spanStart < minStart) { + minStart = spanStart; + } + } + return minStart; + } + + boolean areAllEndsEqual() { + int end = mSpans[0].getEndLine(Span.INVALID_LINE); + for (int i = 1; i < mSpanCount; i++) { + if (mSpans[i].getEndLine(Span.INVALID_LINE) != end) { + return false; + } + } + return true; + } + + boolean areAllStartsEqual() { + int start = mSpans[0].getStartLine(Span.INVALID_LINE); + for (int i = 1; i < mSpanCount; i++) { + if (mSpans[i].getStartLine(Span.INVALID_LINE) != start) { + return false; + } + } + return true; + } + + private int getMaxEnd(int def) { + int maxEnd = mSpans[0].getEndLine(def); + for (int i = 1; i < mSpanCount; i++) { + final int spanEnd = mSpans[i].getEndLine(def); + if (spanEnd > maxEnd) { + maxEnd = spanEnd; + } + } + return maxEnd; + } + + private int getMinEnd(int def) { + int minEnd = mSpans[0].getEndLine(def); + for (int i = 1; i < mSpanCount; i++) { + final int spanEnd = mSpans[i].getEndLine(def); + if (spanEnd < minEnd) { + minEnd = spanEnd; + } + } + return minEnd; + } + + private void recycleFromStart(RecyclerView.Recycler recycler, int line) { + if (DEBUG) { + Log.d(TAG, "recycling from start for line " + line); + } + while (getChildCount() > 0) { + View child = getChildAt(0); + if (mPrimaryOrientation.getDecoratedEnd(child) < line) { + LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (lp.mFullSpan) { + for (int j = 0; j < mSpanCount; j++) { + mSpans[j].popStart(); + } + } else { + lp.mSpan.popStart(); + } + removeAndRecycleView(child, recycler); + } else { + return;// done + } + } + } + + private void recycleFromEnd(RecyclerView.Recycler recycler, int line) { + final int childCount = getChildCount(); + int i; + for (i = childCount - 1; i >= 0; i--) { + View child = getChildAt(i); + if (mPrimaryOrientation.getDecoratedStart(child) > line) { + LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (lp.mFullSpan) { + for (int j = 0; j < mSpanCount; j++) { + mSpans[j].popEnd(); + } + } else { + lp.mSpan.popEnd(); + } + removeAndRecycleView(child, recycler); + } else { + return;// done + } + } + } + + /** + * @return True if last span is the first one we want to fill + */ + private boolean preferLastSpan(int layoutDir) { + if (mOrientation == HORIZONTAL) { + return (layoutDir == LAYOUT_START) != mShouldReverseLayout; + } + return ((layoutDir == LAYOUT_START) == mShouldReverseLayout) == isLayoutRTL(); + } + + /** + * Finds the span for the next view. + */ + private Span getNextSpan(LayoutState layoutState) { + final boolean preferLastSpan = preferLastSpan(layoutState.mLayoutDirection); + final int startIndex, endIndex, diff; + if (preferLastSpan) { + startIndex = mSpanCount - 1; + endIndex = -1; + diff = -1; + } else { + startIndex = 0; + endIndex = mSpanCount; + diff = 1; + } + if (layoutState.mLayoutDirection == LAYOUT_END) { + Span min = null; + int minLine = Integer.MAX_VALUE; + final int defaultLine = mPrimaryOrientation.getStartAfterPadding(); + for (int i = startIndex; i != endIndex; i += diff) { + final Span other = mSpans[i]; + int otherLine = other.getEndLine(defaultLine); + if (otherLine < minLine) { + min = other; + minLine = otherLine; + } + } + return min; + } else { + Span max = null; + int maxLine = Integer.MIN_VALUE; + final int defaultLine = mPrimaryOrientation.getEndAfterPadding(); + for (int i = startIndex; i != endIndex; i += diff) { + final Span other = mSpans[i]; + int otherLine = other.getStartLine(defaultLine); + if (otherLine > maxLine) { + max = other; + maxLine = otherLine; + } + } + return max; + } + } + + @Override + public boolean canScrollVertically() { + return mOrientation == VERTICAL; + } + + @Override + public boolean canScrollHorizontally() { + return mOrientation == HORIZONTAL; + } + + @Override + public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, + RecyclerView.State state) { + return scrollBy(dx, recycler, state); + } + + @Override + public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, + RecyclerView.State state) { + return scrollBy(dy, recycler, state); + } + + private int calculateScrollDirectionForPosition(int position) { + if (getChildCount() == 0) { + return mShouldReverseLayout ? LAYOUT_END : LAYOUT_START; + } + final int firstChildPos = getFirstChildPosition(); + return position < firstChildPos != mShouldReverseLayout ? LAYOUT_START : LAYOUT_END; + } + + @Override + public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, + int position) { + LinearSmoothScroller scroller = new LinearSmoothScroller(recyclerView.getContext()) { + @Override + public PointF computeScrollVectorForPosition(int targetPosition) { + final int direction = calculateScrollDirectionForPosition(targetPosition); + if (direction == 0) { + return null; + } + if (mOrientation == HORIZONTAL) { + return new PointF(direction, 0); + } else { + return new PointF(0, direction); + } + } + }; + scroller.setTargetPosition(position); + startSmoothScroll(scroller); + } + + @Override + public void scrollToPosition(int position) { + if (mPendingSavedState != null && mPendingSavedState.mAnchorPosition != position) { + mPendingSavedState.invalidateAnchorPositionInfo(); + } + mPendingScrollPosition = position; + mPendingScrollPositionOffset = INVALID_OFFSET; + requestLayout(); + } + + /** + * Scroll to the specified adapter position with the given offset from layout start. + *

+ * Note that scroll position change will not be reflected until the next layout call. + *

+ * If you are just trying to make a position visible, use {@link #scrollToPosition(int)}. + * + * @param position Index (starting at 0) of the reference item. + * @param offset The distance (in pixels) between the start edge of the item view and + * start edge of the RecyclerView. + * @see #setReverseLayout(boolean) + * @see #scrollToPosition(int) + */ + public void scrollToPositionWithOffset(int position, int offset) { + if (mPendingSavedState != null) { + mPendingSavedState.invalidateAnchorPositionInfo(); + } + mPendingScrollPosition = position; + mPendingScrollPositionOffset = offset; + requestLayout(); + } + + int scrollBy(int dt, RecyclerView.Recycler recycler, RecyclerView.State state) { + ensureOrientationHelper(); + final int referenceChildPosition; + if (dt > 0) { // layout towards end + mLayoutState.mLayoutDirection = LAYOUT_END; + mLayoutState.mItemDirection = mShouldReverseLayout ? ITEM_DIRECTION_HEAD + : ITEM_DIRECTION_TAIL; + referenceChildPosition = getLastChildPosition(); + } else { + mLayoutState.mLayoutDirection = LAYOUT_START; + mLayoutState.mItemDirection = mShouldReverseLayout ? ITEM_DIRECTION_TAIL + : ITEM_DIRECTION_HEAD; + referenceChildPosition = getFirstChildPosition(); + } + mLayoutState.mCurrentPosition = referenceChildPosition + mLayoutState.mItemDirection; + final int absDt = Math.abs(dt); + mLayoutState.mAvailable = absDt; + mLayoutState.mExtra = isSmoothScrolling() ? mPrimaryOrientation.getTotalSpace() : 0; + int consumed = fill(recycler, mLayoutState, state); + final int totalScroll; + if (absDt < consumed) { + totalScroll = dt; + } else if (dt < 0) { + totalScroll = -consumed; + } else { // dt > 0 + totalScroll = consumed; + } + if (DEBUG) { + Log.d(TAG, "asked " + dt + " scrolled" + totalScroll); + } + + mPrimaryOrientation.offsetChildren(-totalScroll); + // always reset this if we scroll for a proper save instance state + mLastLayoutFromEnd = mShouldReverseLayout; + return totalScroll; + } + + private int getLastChildPosition() { + final int childCount = getChildCount(); + return childCount == 0 ? 0 : getPosition(getChildAt(childCount - 1)); + } + + private int getFirstChildPosition() { + final int childCount = getChildCount(); + return childCount == 0 ? 0 : getPosition(getChildAt(0)); + } + + /** + * Finds the first View that can be used as an anchor View. + * + * @return Position of the View or 0 if it cannot find any such View. + */ + private int findFirstReferenceChildPosition(int itemCount) { + final int limit = getChildCount(); + for (int i = 0; i < limit; i++) { + final View view = getChildAt(i); + final int position = getPosition(view); + if (position >= 0 && position < itemCount) { + return position; + } + } + return 0; + } + + /** + * Finds the last View that can be used as an anchor View. + * + * @return Position of the View or 0 if it cannot find any such View. + */ + private int findLastReferenceChildPosition(int itemCount) { + for (int i = getChildCount() - 1; i >= 0; i--) { + final View view = getChildAt(i); + final int position = getPosition(view); + if (position >= 0 && position < itemCount) { + return position; + } + } + return 0; + } + + @Override + public RecyclerView.LayoutParams generateDefaultLayoutParams() { + return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT); + } + + @Override + public RecyclerView.LayoutParams generateLayoutParams(Context c, AttributeSet attrs) { + return new LayoutParams(c, attrs); + } + + @Override + public RecyclerView.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { + if (lp instanceof ViewGroup.MarginLayoutParams) { + return new LayoutParams((ViewGroup.MarginLayoutParams) lp); + } else { + return new LayoutParams(lp); + } + } + + @Override + public boolean checkLayoutParams(RecyclerView.LayoutParams lp) { + return lp instanceof LayoutParams; + } + + public int getOrientation() { + return mOrientation; + } + + + /** + * LayoutParams used by StaggeredGridLayoutManager. + *

+ * Note that if the orientation is {@link #VERTICAL}, the width parameter is ignored and if the + * orientation is {@link #HORIZONTAL} the height parameter is ignored because child view is + * expected to fill all of the space given to it. + */ + public static class LayoutParams extends RecyclerView.LayoutParams { + + /** + * Span Id for Views that are not laid out yet. + */ + public static final int INVALID_SPAN_ID = -1; + + // Package scope to be able to access from tests. + Span mSpan; + + boolean mFullSpan; + + public LayoutParams(Context c, AttributeSet attrs) { + super(c, attrs); + } + + public LayoutParams(int width, int height) { + super(width, height); + } + + public LayoutParams(ViewGroup.MarginLayoutParams source) { + super(source); + } + + public LayoutParams(ViewGroup.LayoutParams source) { + super(source); + } + + public LayoutParams(RecyclerView.LayoutParams source) { + super(source); + } + + /** + * When set to true, the item will layout using all span area. That means, if orientation + * is vertical, the view will have full width; if orientation is horizontal, the view will + * have full height. + * + * @param fullSpan True if this item should traverse all spans. + * @see #isFullSpan() + */ + public void setFullSpan(boolean fullSpan) { + mFullSpan = fullSpan; + } + + /** + * Returns whether this View occupies all available spans or just one. + * + * @return True if the View occupies all spans or false otherwise. + * @see #setFullSpan(boolean) + */ + public boolean isFullSpan() { + return mFullSpan; + } + + /** + * Returns the Span index to which this View is assigned. + * + * @return The Span index of the View. If View is not yet assigned to any span, returns + * {@link #INVALID_SPAN_ID}. + */ + public final int getSpanIndex() { + if (mSpan == null) { + return INVALID_SPAN_ID; + } + return mSpan.mIndex; + } + } + + // Package scoped to access from tests. + class Span { + + static final int INVALID_LINE = Integer.MIN_VALUE; + private ArrayList mViews = new ArrayList(); + int mCachedStart = INVALID_LINE; + int mCachedEnd = INVALID_LINE; + int mDeletedSize = 0; + final int mIndex; + + private Span(int index) { + mIndex = index; + } + + int getStartLine(int def) { + if (mCachedStart != INVALID_LINE) { + return mCachedStart; + } + if (mViews.size() == 0) { + return def; + } + calculateCachedStart(); + return mCachedStart; + } + + void calculateCachedStart() { + final View startView = mViews.get(0); + final LayoutParams lp = getLayoutParams(startView); + mCachedStart = mPrimaryOrientation.getDecoratedStart(startView); + if (lp.mFullSpan) { + LazySpanLookup.FullSpanItem fsi = mLazySpanLookup + .getFullSpanItem(lp.getViewLayoutPosition()); + if (fsi != null && fsi.mGapDir == LAYOUT_START) { + mCachedStart -= fsi.getGapForSpan(mIndex); + } + } + } + + // Use this one when default value does not make sense and not having a value means a bug. + int getStartLine() { + if (mCachedStart != INVALID_LINE) { + return mCachedStart; + } + calculateCachedStart(); + return mCachedStart; + } + + int getEndLine(int def) { + if (mCachedEnd != INVALID_LINE) { + return mCachedEnd; + } + final int size = mViews.size(); + if (size == 0) { + return def; + } + calculateCachedEnd(); + return mCachedEnd; + } + + void calculateCachedEnd() { + final View endView = mViews.get(mViews.size() - 1); + final LayoutParams lp = getLayoutParams(endView); + mCachedEnd = mPrimaryOrientation.getDecoratedEnd(endView); + if (lp.mFullSpan) { + LazySpanLookup.FullSpanItem fsi = mLazySpanLookup + .getFullSpanItem(lp.getViewLayoutPosition()); + if (fsi != null && fsi.mGapDir == LAYOUT_END) { + mCachedEnd += fsi.getGapForSpan(mIndex); + } + } + } + + // Use this one when default value does not make sense and not having a value means a bug. + int getEndLine() { + if (mCachedEnd != INVALID_LINE) { + return mCachedEnd; + } + calculateCachedEnd(); + return mCachedEnd; + } + + void prependToSpan(View view) { + LayoutParams lp = getLayoutParams(view); + lp.mSpan = this; + mViews.add(0, view); + mCachedStart = INVALID_LINE; + if (mViews.size() == 1) { + mCachedEnd = INVALID_LINE; + } + if (lp.isItemRemoved() || lp.isItemChanged()) { + mDeletedSize += mPrimaryOrientation.getDecoratedMeasurement(view); + } + } + + void appendToSpan(View view) { + LayoutParams lp = getLayoutParams(view); + lp.mSpan = this; + mViews.add(view); + mCachedEnd = INVALID_LINE; + if (mViews.size() == 1) { + mCachedStart = INVALID_LINE; + } + if (lp.isItemRemoved() || lp.isItemChanged()) { + mDeletedSize += mPrimaryOrientation.getDecoratedMeasurement(view); + } + } + + // Useful method to preserve positions on a re-layout. + void cacheReferenceLineAndClear(boolean reverseLayout, int offset) { + int reference; + if (reverseLayout) { + reference = getEndLine(INVALID_LINE); + } else { + reference = getStartLine(INVALID_LINE); + } + clear(); + if (reference == INVALID_LINE) { + return; + } + if ((reverseLayout && reference < mPrimaryOrientation.getEndAfterPadding()) || + (!reverseLayout && reference > mPrimaryOrientation.getStartAfterPadding())) { + return; + } + if (offset != INVALID_OFFSET) { + reference += offset; + } + mCachedStart = mCachedEnd = reference; + } + + void clear() { + mViews.clear(); + invalidateCache(); + mDeletedSize = 0; + } + + void invalidateCache() { + mCachedStart = INVALID_LINE; + mCachedEnd = INVALID_LINE; + } + + void setLine(int line) { + mCachedEnd = mCachedStart = line; + } + + void popEnd() { + final int size = mViews.size(); + View end = mViews.remove(size - 1); + final LayoutParams lp = getLayoutParams(end); + lp.mSpan = null; + if (lp.isItemRemoved() || lp.isItemChanged()) { + mDeletedSize -= mPrimaryOrientation.getDecoratedMeasurement(end); + } + if (size == 1) { + mCachedStart = INVALID_LINE; + } + mCachedEnd = INVALID_LINE; + } + + void popStart() { + View start = mViews.remove(0); + final LayoutParams lp = getLayoutParams(start); + lp.mSpan = null; + if (mViews.size() == 0) { + mCachedEnd = INVALID_LINE; + } + if (lp.isItemRemoved() || lp.isItemChanged()) { + mDeletedSize -= mPrimaryOrientation.getDecoratedMeasurement(start); + } + mCachedStart = INVALID_LINE; + } + + public int getDeletedSize() { + return mDeletedSize; + } + + LayoutParams getLayoutParams(View view) { + return (LayoutParams) view.getLayoutParams(); + } + + void onOffset(int dt) { + if (mCachedStart != INVALID_LINE) { + mCachedStart += dt; + } + if (mCachedEnd != INVALID_LINE) { + mCachedEnd += dt; + } + } + + // normalized offset is how much this span can scroll + int getNormalizedOffset(int dt, int targetStart, int targetEnd) { + if (mViews.size() == 0) { + return 0; + } + if (dt < 0) { + final int endSpace = getEndLine() - targetEnd; + if (endSpace <= 0) { + return 0; + } + return -dt > endSpace ? -endSpace : dt; + } else { + final int startSpace = targetStart - getStartLine(); + if (startSpace <= 0) { + return 0; + } + return startSpace < dt ? startSpace : dt; + } + } + + /** + * Returns if there is no child between start-end lines + * + * @param start The start line + * @param end The end line + * @return true if a new child can be added between start and end + */ + boolean isEmpty(int start, int end) { + final int count = mViews.size(); + for (int i = 0; i < count; i++) { + final View view = mViews.get(i); + if (mPrimaryOrientation.getDecoratedStart(view) < end && + mPrimaryOrientation.getDecoratedEnd(view) > start) { + return false; + } + } + return true; + } + + public int findFirstVisibleItemPosition() { + return mReverseLayout + ? findOneVisibleChild(mViews.size() - 1, -1, false) + : findOneVisibleChild(0, mViews.size(), false); + } + + public int findFirstCompletelyVisibleItemPosition() { + return mReverseLayout + ? findOneVisibleChild(mViews.size() - 1, -1, true) + : findOneVisibleChild(0, mViews.size(), true); + } + + public int findLastVisibleItemPosition() { + return mReverseLayout + ? findOneVisibleChild(0, mViews.size(), false) + : findOneVisibleChild(mViews.size() - 1, -1, false); + } + + public int findLastCompletelyVisibleItemPosition() { + return mReverseLayout + ? findOneVisibleChild(0, mViews.size(), true) + : findOneVisibleChild(mViews.size() - 1, -1, true); + } + + int findOneVisibleChild(int fromIndex, int toIndex, boolean completelyVisible) { + final int start = mPrimaryOrientation.getStartAfterPadding(); + final int end = mPrimaryOrientation.getEndAfterPadding(); + final int next = toIndex > fromIndex ? 1 : -1; + for (int i = fromIndex; i != toIndex; i += next) { + final View child = mViews.get(i); + final int childStart = mPrimaryOrientation.getDecoratedStart(child); + final int childEnd = mPrimaryOrientation.getDecoratedEnd(child); + if (childStart < end && childEnd > start) { + if (completelyVisible) { + if (childStart >= start && childEnd <= end) { + return getPosition(child); + } + } else { + return getPosition(child); + } + } + } + return NO_POSITION; + } + } + + /** + * An array of mappings from adapter position to span. + * This only grows when a write happens and it grows up to the size of the adapter. + */ + static class LazySpanLookup { + + private static final int MIN_SIZE = 10; + int[] mData; + List mFullSpanItems; + + + /** + * Invalidates everything after this position, including full span information + */ + int forceInvalidateAfter(int position) { + if (mFullSpanItems != null) { + for (int i = mFullSpanItems.size() - 1; i >= 0; i--) { + FullSpanItem fsi = mFullSpanItems.get(i); + if (fsi.mPosition >= position) { + mFullSpanItems.remove(i); + } + } + } + return invalidateAfter(position); + } + + /** + * returns end position for invalidation. + */ + int invalidateAfter(int position) { + if (mData == null) { + return RecyclerView.NO_POSITION; + } + if (position >= mData.length) { + return RecyclerView.NO_POSITION; + } + int endPosition = invalidateFullSpansAfter(position); + if (endPosition == RecyclerView.NO_POSITION) { + Arrays.fill(mData, position, mData.length, LayoutParams.INVALID_SPAN_ID); + return mData.length; + } else { + // just invalidate items in between + Arrays.fill(mData, position, endPosition + 1, LayoutParams.INVALID_SPAN_ID); + return endPosition + 1; + } + } + + int getSpan(int position) { + if (mData == null || position >= mData.length) { + return LayoutParams.INVALID_SPAN_ID; + } else { + return mData[position]; + } + } + + void setSpan(int position, Span span) { + ensureSize(position); + mData[position] = span.mIndex; + } + + int sizeForPosition(int position) { + int len = mData.length; + while (len <= position) { + len *= 2; + } + return len; + } + + void ensureSize(int position) { + if (mData == null) { + mData = new int[Math.max(position, MIN_SIZE) + 1]; + Arrays.fill(mData, LayoutParams.INVALID_SPAN_ID); + } else if (position >= mData.length) { + int[] old = mData; + mData = new int[sizeForPosition(position)]; + System.arraycopy(old, 0, mData, 0, old.length); + Arrays.fill(mData, old.length, mData.length, LayoutParams.INVALID_SPAN_ID); + } + } + + void clear() { + if (mData != null) { + Arrays.fill(mData, LayoutParams.INVALID_SPAN_ID); + } + mFullSpanItems = null; + } + + void offsetForRemoval(int positionStart, int itemCount) { + if (mData == null || positionStart >= mData.length) { + return; + } + ensureSize(positionStart + itemCount); + System.arraycopy(mData, positionStart + itemCount, mData, positionStart, + mData.length - positionStart - itemCount); + Arrays.fill(mData, mData.length - itemCount, mData.length, + LayoutParams.INVALID_SPAN_ID); + offsetFullSpansForRemoval(positionStart, itemCount); + } + + private void offsetFullSpansForRemoval(int positionStart, int itemCount) { + if (mFullSpanItems == null) { + return; + } + final int end = positionStart + itemCount; + for (int i = mFullSpanItems.size() - 1; i >= 0; i--) { + FullSpanItem fsi = mFullSpanItems.get(i); + if (fsi.mPosition < positionStart) { + continue; + } + if (fsi.mPosition < end) { + mFullSpanItems.remove(i); + } else { + fsi.mPosition -= itemCount; + } + } + } + + void offsetForAddition(int positionStart, int itemCount) { + if (mData == null || positionStart >= mData.length) { + return; + } + ensureSize(positionStart + itemCount); + System.arraycopy(mData, positionStart, mData, positionStart + itemCount, + mData.length - positionStart - itemCount); + Arrays.fill(mData, positionStart, positionStart + itemCount, + LayoutParams.INVALID_SPAN_ID); + offsetFullSpansForAddition(positionStart, itemCount); + } + + private void offsetFullSpansForAddition(int positionStart, int itemCount) { + if (mFullSpanItems == null) { + return; + } + for (int i = mFullSpanItems.size() - 1; i >= 0; i--) { + FullSpanItem fsi = mFullSpanItems.get(i); + if (fsi.mPosition < positionStart) { + continue; + } + fsi.mPosition += itemCount; + } + } + + /** + * Returns when invalidation should end. e.g. hitting a full span position. + * Returned position SHOULD BE invalidated. + */ + private int invalidateFullSpansAfter(int position) { + if (mFullSpanItems == null) { + return RecyclerView.NO_POSITION; + } + final FullSpanItem item = getFullSpanItem(position); + // if there is an fsi at this position, get rid of it. + if (item != null) { + mFullSpanItems.remove(item); + } + int nextFsiIndex = -1; + final int count = mFullSpanItems.size(); + for (int i = 0; i < count; i++) { + FullSpanItem fsi = mFullSpanItems.get(i); + if (fsi.mPosition >= position) { + nextFsiIndex = i; + break; + } + } + if (nextFsiIndex != -1) { + FullSpanItem fsi = mFullSpanItems.get(nextFsiIndex); + mFullSpanItems.remove(nextFsiIndex); + return fsi.mPosition; + } + return RecyclerView.NO_POSITION; + } + + public void addFullSpanItem(FullSpanItem fullSpanItem) { + if (mFullSpanItems == null) { + mFullSpanItems = new ArrayList(); + } + final int size = mFullSpanItems.size(); + for (int i = 0; i < size; i++) { + FullSpanItem other = mFullSpanItems.get(i); + if (other.mPosition == fullSpanItem.mPosition) { + if (DEBUG) { + throw new IllegalStateException("two fsis for same position"); + } else { + mFullSpanItems.remove(i); + } + } + if (other.mPosition >= fullSpanItem.mPosition) { + mFullSpanItems.add(i, fullSpanItem); + return; + } + } + // if it is not added to a position. + mFullSpanItems.add(fullSpanItem); + } + + public FullSpanItem getFullSpanItem(int position) { + if (mFullSpanItems == null) { + return null; + } + for (int i = mFullSpanItems.size() - 1; i >= 0; i--) { + final FullSpanItem fsi = mFullSpanItems.get(i); + if (fsi.mPosition == position) { + return fsi; + } + } + return null; + } + + /** + * @param minPos inclusive + * @param maxPos exclusive + * @param gapDir if not 0, returns FSIs on in that direction + * @param hasUnwantedGapAfter If true, when full span item has unwanted gaps, it will be + * returned even if its gap direction does not match. + */ + public FullSpanItem getFirstFullSpanItemInRange(int minPos, int maxPos, int gapDir, + boolean hasUnwantedGapAfter) { + if (mFullSpanItems == null) { + return null; + } + final int limit = mFullSpanItems.size(); + for (int i = 0; i < limit; i++) { + FullSpanItem fsi = mFullSpanItems.get(i); + if (fsi.mPosition >= maxPos) { + return null; + } + if (fsi.mPosition >= minPos + && (gapDir == 0 || fsi.mGapDir == gapDir || + (hasUnwantedGapAfter && fsi.mHasUnwantedGapAfter))) { + return fsi; + } + } + return null; + } + + /** + * We keep information about full span items because they may create gaps in the UI. + */ + static class FullSpanItem implements Parcelable { + + int mPosition; + int mGapDir; + int[] mGapPerSpan; + // A full span may be laid out in primary direction but may have gaps due to + // invalidation of views after it. This is recorded during a reverse scroll and if + // view is still on the screen after scroll stops, we have to recalculate layout + boolean mHasUnwantedGapAfter; + + public FullSpanItem(Parcel in) { + mPosition = in.readInt(); + mGapDir = in.readInt(); + mHasUnwantedGapAfter = in.readInt() == 1; + int spanCount = in.readInt(); + if (spanCount > 0) { + mGapPerSpan = new int[spanCount]; + in.readIntArray(mGapPerSpan); + } + } + + public FullSpanItem() { + } + + int getGapForSpan(int spanIndex) { + return mGapPerSpan == null ? 0 : mGapPerSpan[spanIndex]; + } + + public void invalidateSpanGaps() { + mGapPerSpan = null; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mPosition); + dest.writeInt(mGapDir); + dest.writeInt(mHasUnwantedGapAfter ? 1 : 0); + if (mGapPerSpan != null && mGapPerSpan.length > 0) { + dest.writeInt(mGapPerSpan.length); + dest.writeIntArray(mGapPerSpan); + } else { + dest.writeInt(0); + } + } + + @Override + public String toString() { + return "FullSpanItem{" + + "mPosition=" + mPosition + + ", mGapDir=" + mGapDir + + ", mHasUnwantedGapAfter=" + mHasUnwantedGapAfter + + ", mGapPerSpan=" + Arrays.toString(mGapPerSpan) + + '}'; + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public FullSpanItem createFromParcel(Parcel in) { + return new FullSpanItem(in); + } + + @Override + public FullSpanItem[] newArray(int size) { + return new FullSpanItem[size]; + } + }; + } + } + + static class SavedState implements Parcelable { + + int mAnchorPosition; + int mVisibleAnchorPosition; // Replacement for span info when spans are invalidated + int mSpanOffsetsSize; + int[] mSpanOffsets; + int mSpanLookupSize; + int[] mSpanLookup; + List mFullSpanItems; + boolean mReverseLayout; + boolean mAnchorLayoutFromEnd; + boolean mLastLayoutRTL; + + public SavedState() { + } + + SavedState(Parcel in) { + mAnchorPosition = in.readInt(); + mVisibleAnchorPosition = in.readInt(); + mSpanOffsetsSize = in.readInt(); + if (mSpanOffsetsSize > 0) { + mSpanOffsets = new int[mSpanOffsetsSize]; + in.readIntArray(mSpanOffsets); + } + + mSpanLookupSize = in.readInt(); + if (mSpanLookupSize > 0) { + mSpanLookup = new int[mSpanLookupSize]; + in.readIntArray(mSpanLookup); + } + mReverseLayout = in.readInt() == 1; + mAnchorLayoutFromEnd = in.readInt() == 1; + mLastLayoutRTL = in.readInt() == 1; + mFullSpanItems = in.readArrayList( + LazySpanLookup.FullSpanItem.class.getClassLoader()); + } + + public SavedState(SavedState other) { + mSpanOffsetsSize = other.mSpanOffsetsSize; + mAnchorPosition = other.mAnchorPosition; + mVisibleAnchorPosition = other.mVisibleAnchorPosition; + mSpanOffsets = other.mSpanOffsets; + mSpanLookupSize = other.mSpanLookupSize; + mSpanLookup = other.mSpanLookup; + mReverseLayout = other.mReverseLayout; + mAnchorLayoutFromEnd = other.mAnchorLayoutFromEnd; + mLastLayoutRTL = other.mLastLayoutRTL; + mFullSpanItems = other.mFullSpanItems; + } + + void invalidateSpanInfo() { + mSpanOffsets = null; + mSpanOffsetsSize = 0; + mSpanLookupSize = 0; + mSpanLookup = null; + mFullSpanItems = null; + } + + void invalidateAnchorPositionInfo() { + mSpanOffsets = null; + mSpanOffsetsSize = 0; + mAnchorPosition = NO_POSITION; + mVisibleAnchorPosition = NO_POSITION; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mAnchorPosition); + dest.writeInt(mVisibleAnchorPosition); + dest.writeInt(mSpanOffsetsSize); + if (mSpanOffsetsSize > 0) { + dest.writeIntArray(mSpanOffsets); + } + dest.writeInt(mSpanLookupSize); + if (mSpanLookupSize > 0) { + dest.writeIntArray(mSpanLookup); + } + dest.writeInt(mReverseLayout ? 1 : 0); + dest.writeInt(mAnchorLayoutFromEnd ? 1 : 0); + dest.writeInt(mLastLayoutRTL ? 1 : 0); + dest.writeList(mFullSpanItems); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + @Override + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } + + /** + * Data class to hold the information about an anchor position which is used in onLayout call. + */ + private class AnchorInfo { + + int mPosition; + int mOffset; + boolean mLayoutFromEnd; + boolean mInvalidateOffsets; + + void reset() { + mPosition = NO_POSITION; + mOffset = INVALID_OFFSET; + mLayoutFromEnd = false; + mInvalidateOffsets = false; + } + + void assignCoordinateFromPadding() { + mOffset = mLayoutFromEnd ? mPrimaryOrientation.getEndAfterPadding() + : mPrimaryOrientation.getStartAfterPadding(); + } + + void assignCoordinateFromPadding(int addedDistance) { + if (mLayoutFromEnd) { + mOffset = mPrimaryOrientation.getEndAfterPadding() - addedDistance; + } else { + mOffset = mPrimaryOrientation.getStartAfterPadding() + addedDistance; + } + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/android/support/widget/util/SortedListAdapterCallback.java b/TMessagesProj/src/main/java/org/telegram/android/support/widget/util/SortedListAdapterCallback.java new file mode 100644 index 000000000..44ddd667e --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/android/support/widget/util/SortedListAdapterCallback.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.telegram.android.support.widget.util; + +import android.support.v7.util.SortedList; +import org.telegram.android.support.widget.RecyclerView; + +/** + * A {@link SortedList.Callback} implementation that can bind a {@link SortedList} to a + * {@link RecyclerView.Adapter}. + */ +public abstract class SortedListAdapterCallback extends SortedList.Callback { + + final RecyclerView.Adapter mAdapter; + + /** + * Creates a {@link SortedList.Callback} that will forward data change events to the provided + * Adapter. + * + * @param adapter The Adapter instance which should receive events from the SortedList. + */ + public SortedListAdapterCallback(RecyclerView.Adapter adapter) { + mAdapter = adapter; + } + + @Override + public void onInserted(int position, int count) { + mAdapter.notifyItemRangeInserted(position, count); + } + + @Override + public void onRemoved(int position, int count) { + mAdapter.notifyItemRangeRemoved(position, count); + } + + @Override + public void onMoved(int fromPosition, int toPosition) { + mAdapter.notifyItemMoved(fromPosition, toPosition); + } + + @Override + public void onChanged(int position, int count) { + mAdapter.notifyItemRangeChanged(position, count); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/AbsSerializedData.java b/TMessagesProj/src/main/java/org/telegram/messenger/AbsSerializedData.java index 0f0bb480c..ae3863940 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/AbsSerializedData.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/AbsSerializedData.java @@ -1,9 +1,9 @@ /* - * This is the source code of Telegram for Android v. 1.3.x. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013-2014. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; @@ -20,17 +20,19 @@ public abstract class AbsSerializedData { public abstract void writeByteArray(byte[] b, int offset, int count); public abstract void writeByteArray(byte[] b); public abstract void writeDouble(double d); - public abstract int readInt32(); - public abstract int readInt32(boolean[] error); - public abstract boolean readBool(); - public abstract long readInt64(); - public abstract long readInt64(boolean[] error); - public abstract void readRaw(byte[] b); - public abstract byte[] readData(int count); - public abstract String readString(); - public abstract byte[] readByteArray(); - public abstract ByteBufferDesc readByteBuffer(); public abstract void writeByteBuffer(ByteBufferDesc buffer); - public abstract double readDouble(); + + public abstract int readInt32(boolean exception); + public abstract boolean readBool(boolean exception); + public abstract long readInt64(boolean exception); + public abstract void readRaw(byte[] b, boolean exception); + public abstract byte[] readData(int count, boolean exception); + public abstract String readString(boolean exception); + public abstract byte[] readByteArray(boolean exception); + public abstract ByteBufferDesc readByteBuffer(boolean exception); + public abstract double readDouble(boolean exception); + public abstract int length(); + public abstract void skip(int count); + public abstract int getPosition(); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/Action.java b/TMessagesProj/src/main/java/org/telegram/messenger/Action.java index 61ed9b70d..dc0c669a4 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/Action.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/Action.java @@ -1,9 +1,9 @@ /* - * This is the source code of Telegram for Android v. 1.3.2. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java index 481be8415..c7b0937a6 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java @@ -1,9 +1,9 @@ /* - * This is the source code of Telegram for Android v. 1.3.2. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; @@ -17,8 +17,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; @@ -159,6 +157,7 @@ public class ApplicationLoader extends Application { } UserConfig.loadConfig(); + MessagesController.getInstance(); if (UserConfig.getCurrentUser() != null) { MessagesController.getInstance().putUser(UserConfig.getCurrentUser(), true); ConnectionsManager.getInstance().applyCountryPortNumber(UserConfig.getCurrentUser().phone); @@ -273,8 +272,7 @@ public class ApplicationLoader extends Application { return ""; } int registeredVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE); - int currentVersion = getAppVersion(); - if (registeredVersion != currentVersion) { + if (registeredVersion != BuildVars.BUILD_VERSION) { FileLog.d("tmessages", "App version changed."); return ""; } @@ -285,15 +283,6 @@ public class ApplicationLoader extends Application { return getSharedPreferences(ApplicationLoader.class.getSimpleName(), Context.MODE_PRIVATE); } - public static int getAppVersion() { - try { - PackageInfo packageInfo = applicationContext.getPackageManager().getPackageInfo(applicationContext.getPackageName(), 0); - return packageInfo.versionCode; - } catch (PackageManager.NameNotFoundException e) { - throw new RuntimeException("Could not get package name: " + e); - } - } - private void registerInBackground() { AsyncTask task = new AsyncTask() { @Override @@ -354,7 +343,7 @@ public class ApplicationLoader extends Application { private void storeRegistrationId(Context context, String regId) { final SharedPreferences prefs = getGCMPreferences(context); - int appVersion = getAppVersion(); + int appVersion = BuildVars.BUILD_VERSION; FileLog.e("tmessages", "Saving regId on app version " + appVersion); SharedPreferences.Editor editor = prefs.edit(); editor.putString(PROPERTY_REG_ID, regId); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/BuffersStorage.java b/TMessagesProj/src/main/java/org/telegram/messenger/BuffersStorage.java index db28c5c73..d6e733709 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/BuffersStorage.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/BuffersStorage.java @@ -1,9 +1,9 @@ /* - * This is the source code of Telegram for Android v. 1.3.x. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013-2014. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; @@ -37,12 +37,12 @@ public class BuffersStorage { public BuffersStorage(boolean threadSafe) { isThreadSafe = threadSafe; - freeBuffers128 = new ArrayList(); - freeBuffers1024 = new ArrayList(); - freeBuffers4096 = new ArrayList(); - freeBuffers16384 = new ArrayList(); - freeBuffers32768 = new ArrayList(); - freeBuffersBig = new ArrayList(); + freeBuffers128 = new ArrayList<>(); + freeBuffers1024 = new ArrayList<>(); + freeBuffers4096 = new ArrayList<>(); + freeBuffers16384 = new ArrayList<>(); + freeBuffers32768 = new ArrayList<>(); + freeBuffersBig = new ArrayList<>(); for (int a = 0; a < 5; a++) { freeBuffers128.add(new ByteBufferDesc(128)); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java b/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java index d2b91b67a..38c0e7ca0 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java @@ -10,10 +10,14 @@ package org.telegram.messenger; public class BuildVars { public static boolean DEBUG_VERSION = false; + public static int BUILD_VERSION = 542; public static int APP_ID = 0; //obtain your own APP_ID at https://core.telegram.org/api/obtaining_api_id public static String APP_HASH = ""; //obtain your own APP_HASH at https://core.telegram.org/api/obtaining_api_id public static String HOCKEY_APP_HASH = "your-hockeyapp-api-key-here"; public static String GCM_SENDER_ID = "760348033672"; public static String SEND_LOGS_EMAIL = "email@gmail.com"; public static String BING_SEARCH_KEY = ""; //obtain your own KEY at https://www.bing.com/dev/en-us/dev-center + public static String FOURSQUARE_API_KEY = ""; //obtain your own KEY at https://developer.foursquare.com/ + public static String FOURSQUARE_API_ID = ""; //obtain your own API_ID at https://developer.foursquare.com/ + public static String FOURSQUARE_API_VERSION = "20150326"; } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ByteArrayOutputStreamExpand.java b/TMessagesProj/src/main/java/org/telegram/messenger/ByteArrayOutputStreamExpand.java new file mode 100644 index 000000000..a4dac2968 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ByteArrayOutputStreamExpand.java @@ -0,0 +1,81 @@ +/* + * This is the source code of Telegram for Android v. 2.x.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2015. + */ + +package org.telegram.messenger; + +import java.io.OutputStream; + +public class ByteArrayOutputStreamExpand extends OutputStream { + + protected byte[] buf; + protected int count; + + public ByteArrayOutputStreamExpand() { + buf = new byte[32]; + } + + public ByteArrayOutputStreamExpand(int size) { + if (size >= 0) { + buf = new byte[size]; + } else { + throw new IllegalArgumentException("size < 0"); + } + } + + private void expand(int i) { + if (count + i <= buf.length) { + return; + } + + byte[] newbuf = new byte[count + i]; + System.arraycopy(buf, 0, newbuf, 0, count); + buf = newbuf; + } + + public synchronized void reset() { + count = 0; + } + + public int size() { + return count; + } + + public byte[] toByteArray() { + return buf; + } + + @Override + public String toString() { + return new String(buf, 0, count); + } + + @Override + public void write(byte[] buffer, int offset, int len) { + checkOffsetAndCount(buffer.length, offset, len); + if (len == 0) { + return; + } + expand(len); + System.arraycopy(buffer, offset, buf, this.count, len); + this.count += len; + } + + @Override + public void write(int oneByte) { + if (count == buf.length) { + expand(1); + } + buf[count++] = (byte) oneByte; + } + + public void checkOffsetAndCount(int arrayLength, int offset, int count) { + if ((offset | count) < 0 || offset > arrayLength || arrayLength - offset < count) { + throw new ArrayIndexOutOfBoundsException("length=" + arrayLength + "; regionStart=" + offset + "; regionLength=" + count); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ByteBufferDesc.java b/TMessagesProj/src/main/java/org/telegram/messenger/ByteBufferDesc.java index f36049bc8..4a63cac25 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ByteBufferDesc.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ByteBufferDesc.java @@ -1,9 +1,9 @@ /* - * This is the source code of Telegram for Android v. 1.3.x. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013-2014. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; @@ -109,7 +109,7 @@ public class ByteBufferDesc extends AbsSerializedData { } else { len += b.length; } - } catch (Exception x) { + } catch (Exception e) { FileLog.e("tmessages", "write raw error"); } } @@ -121,7 +121,7 @@ public class ByteBufferDesc extends AbsSerializedData { } else { len += count; } - } catch (Exception x) { + } catch (Exception e) { FileLog.e("tmessages", "write raw error"); } } @@ -145,7 +145,7 @@ public class ByteBufferDesc extends AbsSerializedData { public void writeString(String s) { try { writeByteArray(s.getBytes("UTF-8")); - } catch(Exception x) { + } catch(Exception e) { FileLog.e("tmessages", "write string error"); } } @@ -182,7 +182,7 @@ public class ByteBufferDesc extends AbsSerializedData { } i++; } - } catch (Exception x) { + } catch (Exception e) { FileLog.e("tmessages", "write byte array error"); } } @@ -219,7 +219,7 @@ public class ByteBufferDesc extends AbsSerializedData { } i++; } - } catch (Exception x) { + } catch (Exception e) { FileLog.e("tmessages", "write byte array error"); } } @@ -227,7 +227,7 @@ public class ByteBufferDesc extends AbsSerializedData { public void writeDouble(double d) { try { writeInt64(Double.doubleToRawLongBits(d)); - } catch(Exception x) { + } catch(Exception e) { FileLog.e("tmessages", "write double error"); } } @@ -280,72 +280,92 @@ public class ByteBufferDesc extends AbsSerializedData { } } - public int readInt32() { - return readInt32(null); + public int getIntFromByte(byte b) { + return b >= 0 ? b : ((int)b) + 256; } - public int readInt32(boolean[] error) { + public int length() { + if (!justCalc) { + return buffer.position(); + } + return len; + } + + public void skip(int count) { + if (count == 0) { + return; + } + if (!justCalc) { + buffer.position(buffer.position() + count); + } else { + len += count; + } + } + + public int getPosition() { + return buffer.position(); + } + + public int readInt32(boolean exception) { try { - int i = buffer.getInt(); - if (error != null) { - error[0] = false; + return buffer.getInt(); + } catch (Exception e) { + if (exception) { + throw new RuntimeException("read int32 error", e); + } else { + FileLog.e("tmessages", "read int32 error"); } - return i; - } catch (Exception x) { - if (error != null) { - error[0] = true; - } - FileLog.e("tmessages", "read int32 error"); } return 0; } - public boolean readBool() { - int consructor = readInt32(); + public boolean readBool(boolean exception) { + int consructor = readInt32(exception); if (consructor == 0x997275b5) { return true; } else if (consructor == 0xbc799737) { return false; } - FileLog.e("tmessages", "Not bool value!"); + if (exception) { + throw new RuntimeException("Not bool value!"); + } else { + FileLog.e("tmessages", "Not bool value!"); + } return false; } - public long readInt64() { - return readInt64(null); - } - - public long readInt64(boolean[] error) { + public long readInt64(boolean exception) { try { - long i = buffer.getLong(); - if (error != null) { - error[0] = false; + return buffer.getLong(); + } catch (Exception e) { + if (exception) { + throw new RuntimeException("read int64 error", e); + } else { + FileLog.e("tmessages", "read int64 error"); } - return i; - } catch (Exception x) { - if (error != null) { - error[0] = true; - } - FileLog.e("tmessages", "read int64 error"); } return 0; } - public void readRaw(byte[] b) { + public void readRaw(byte[] b, boolean exception) { try { buffer.get(b); - } catch (Exception x) { - FileLog.e("tmessages", "read raw error"); + } catch (Exception e) { + if (exception) { + throw new RuntimeException("read raw error", e); + } else { + FileLog.e("tmessages", "read raw error"); + } } } - public byte[] readData(int count) { + public byte[] readData(int count, boolean exception) { byte[] arr = new byte[count]; - readRaw(arr); + readRaw(arr, exception); return arr; } - public String readString() { + public String readString(boolean exception) { try { int sl = 1; int l = getIntFromByte(buffer.get()); @@ -361,17 +381,17 @@ public class ByteBufferDesc extends AbsSerializedData { i++; } return new String(b, "UTF-8"); - } catch (Exception x) { - FileLog.e("tmessages", "read string error"); + } catch (Exception e) { + if (exception) { + throw new RuntimeException("read string error", e); + } else { + FileLog.e("tmessages", "read string error"); + } } return null; } - public int getIntFromByte(byte b) { - return b >= 0 ? b : ((int)b) + 256; - } - - public byte[] readByteArray() { + public byte[] readByteArray(boolean exception) { try { int sl = 1; int l = getIntFromByte(buffer.get()); @@ -387,13 +407,17 @@ public class ByteBufferDesc extends AbsSerializedData { i++; } return b; - } catch (Exception x) { - FileLog.e("tmessages", "read byte array error"); + } catch (Exception e) { + if (exception) { + throw new RuntimeException("read byte array error", e); + } else { + FileLog.e("tmessages", "read byte array error"); + } } return null; } - public ByteBufferDesc readByteBuffer() { + public ByteBufferDesc readByteBuffer(boolean exception) { try { int sl = 1; int l = getIntFromByte(buffer.get()); @@ -415,25 +439,26 @@ public class ByteBufferDesc extends AbsSerializedData { i++; } return b; - } catch (Exception x) { - FileLog.e("tmessages", "read byte array error"); + } catch (Exception e) { + if (exception) { + throw new RuntimeException("read byte array error", e); + } else { + FileLog.e("tmessages", "read byte array error"); + } } return null; } - public double readDouble() { + public double readDouble(boolean exception) { try { - return Double.longBitsToDouble(readInt64()); - } catch(Exception x) { - FileLog.e("tmessages", "read double error"); + return Double.longBitsToDouble(readInt64(exception)); + } catch(Exception e) { + if (exception) { + throw new RuntimeException("read double error", e); + } else { + FileLog.e("tmessages", "read double error"); + } } return 0; } - - public int length() { - if (!justCalc) { - return buffer.position(); - } - return len; - } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionContext.java b/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionContext.java index b32127dbc..06e654576 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionContext.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionContext.java @@ -1,9 +1,9 @@ /* - * This is the source code of Telegram for Android v. 1.4.x. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013-2014. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java b/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java index 3d8fa2a10..658896f07 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java @@ -1,13 +1,14 @@ /* - * This is the source code of Telegram for Android v. 1.3.2. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; +import android.annotation.SuppressLint; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageInfo; @@ -24,7 +25,13 @@ import org.telegram.android.MessagesController; import org.telegram.android.NotificationCenter; import java.io.File; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.InterfaceAddress; +import java.net.NetworkInterface; import java.util.ArrayList; +import java.util.Enumeration; import java.util.HashMap; import java.util.Locale; import java.util.concurrent.ConcurrentHashMap; @@ -65,7 +72,6 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. private boolean updatingDcSettings = false; private int updatingDcStartTime = 0; private int lastDcUpdateTime = 0; - private int currentAppVersion = 0; private long pushSessionId; private boolean registeringForPush = false; @@ -85,6 +91,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. private PowerManager.WakeLock wakeLock = null; private static volatile ConnectionsManager Instance = null; + public static ConnectionsManager getInstance() { ConnectionsManager localInstance = Instance; if (localInstance == null) { @@ -185,7 +192,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. boolean notFound = true; for (Action actor : actionQueue) { if (actor instanceof HandshakeAction) { - HandshakeAction eactor = (HandshakeAction)actor; + HandshakeAction eactor = (HandshakeAction) actor; if (eactor.datacenter.datacenterId == datacenter.datacenterId) { notFound = false; break; @@ -206,7 +213,6 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. }; public ConnectionsManager() { - currentAppVersion = ApplicationLoader.getAppVersion(); lastOutgoingMessageId = 0; movingToDatacenterId = DEFAULT_DATACENTER_ID; loadSession(); @@ -218,7 +224,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. Utilities.stageQueue.postRunnable(stageRunnable, 1000); try { - PowerManager pm = (PowerManager)ApplicationLoader.applicationContext.getSystemService(Context.POWER_SERVICE); + PowerManager pm = (PowerManager) ApplicationLoader.applicationContext.getSystemService(Context.POWER_SERVICE); wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "lock"); wakeLock.setReferenceCounted(false); } catch (Exception e) { @@ -352,20 +358,20 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. if (configFile.exists()) { try { SerializedData data = new SerializedData(configFile); - isTestBackend = data.readInt32(); - int version = data.readInt32(); + isTestBackend = data.readInt32(false); + int version = data.readInt32(false); sessionsToDestroy.clear(); - int count = data.readInt32(); + int count = data.readInt32(false); for (int a = 0; a < count; a++) { - sessionsToDestroy.add(data.readInt64()); + sessionsToDestroy.add(data.readInt64(false)); } - timeDifference = data.readInt32(); - count = data.readInt32(); + timeDifference = data.readInt32(false); + count = data.readInt32(false); for (int a = 0; a < count; a++) { Datacenter datacenter = new Datacenter(data, 0); datacenters.put(datacenter.datacenterId, datacenter); } - currentDatacenterId = data.readInt32(); + currentDatacenterId = data.readInt32(false); data.cleanup(); } catch (Exception e) { UserConfig.clearConfig(); @@ -385,9 +391,9 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. byte[] sessionsBytes = Base64.decode(sessionsString, Base64.DEFAULT); if (sessionsBytes != null) { SerializedData data = new SerializedData(sessionsBytes); - int count = data.readInt32(); + int count = data.readInt32(false); for (int a = 0; a < count; a++) { - sessionsToDestroy.add(data.readInt64()); + sessionsToDestroy.add(data.readInt64(false)); } data.cleanup(); } @@ -402,7 +408,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. byte[] datacentersBytes = Base64.decode(datacentersString, Base64.DEFAULT); if (datacentersBytes != null) { SerializedData data = new SerializedData(datacentersBytes); - int count = data.readInt32(); + int count = data.readInt32(false); for (int a = 0; a < count; a++) { Datacenter datacenter = new Datacenter(data, 1); datacenters.put(datacenter.datacenterId, datacenter); @@ -445,67 +451,143 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. if (isTestBackend == 0) { Datacenter datacenter = new Datacenter(); datacenter.datacenterId = 1; - datacenter.addAddressAndPort("149.154.175.50", 443); + datacenter.addAddressAndPort("149.154.175.50", 443, 0); + datacenter.addAddressAndPort("2001:b28:f23d:f001:0000:0000:0000:000a", 443, 1); datacenters.put(datacenter.datacenterId, datacenter); datacenter = new Datacenter(); datacenter.datacenterId = 2; - datacenter.addAddressAndPort("149.154.167.51", 443); + datacenter.addAddressAndPort("149.154.167.51", 443, 0); + datacenter.addAddressAndPort("2001:67c:4e8:f002:0000:0000:0000:000a", 443, 1); datacenters.put(datacenter.datacenterId, datacenter); datacenter = new Datacenter(); datacenter.datacenterId = 3; - datacenter.addAddressAndPort("149.154.175.100", 443); + datacenter.addAddressAndPort("149.154.175.100", 443, 0); + datacenter.addAddressAndPort("2001:b28:f23d:f003:0000:0000:0000:000a", 443, 1); datacenters.put(datacenter.datacenterId, datacenter); datacenter = new Datacenter(); datacenter.datacenterId = 4; - datacenter.addAddressAndPort("149.154.167.91", 443); + datacenter.addAddressAndPort("149.154.167.91", 443, 0); + datacenter.addAddressAndPort("2001:67c:4e8:f004:0000:0000:0000:000a", 443, 1); datacenters.put(datacenter.datacenterId, datacenter); datacenter = new Datacenter(); datacenter.datacenterId = 5; - datacenter.addAddressAndPort("149.154.171.5", 443); + datacenter.addAddressAndPort("149.154.171.5", 443, 0); + datacenter.addAddressAndPort("2001:b28:f23f:f005:0000:0000:0000:000a", 443, 1); datacenters.put(datacenter.datacenterId, datacenter); } else { Datacenter datacenter = new Datacenter(); datacenter.datacenterId = 1; - datacenter.addAddressAndPort("149.154.175.10", 443); + datacenter.addAddressAndPort("149.154.175.10", 443, 0); + datacenter.addAddressAndPort("2001:b28:f23d:f001:0000:0000:0000:000e", 443, 1); datacenters.put(datacenter.datacenterId, datacenter); datacenter = new Datacenter(); datacenter.datacenterId = 2; - datacenter.addAddressAndPort("149.154.167.40", 443); + datacenter.addAddressAndPort("149.154.167.40", 443, 0); + datacenter.addAddressAndPort("2001:67c:4e8:f002:0000:0000:0000:000e", 443, 1); datacenters.put(datacenter.datacenterId, datacenter); datacenter = new Datacenter(); datacenter.datacenterId = 3; - datacenter.addAddressAndPort("149.154.175.117", 443); + datacenter.addAddressAndPort("149.154.175.117", 443, 0); + datacenter.addAddressAndPort("2001:b28:f23d:f003:0000:0000:0000:000e", 443, 1); datacenters.put(datacenter.datacenterId, datacenter); } } else if (datacenters.size() == 1) { Datacenter datacenter = new Datacenter(); datacenter.datacenterId = 2; - datacenter.addAddressAndPort("149.154.167.51", 443); + datacenter.addAddressAndPort("149.154.167.51", 443, 0); + datacenter.addAddressAndPort("2001:67c:4e8:f002:0000:0000:0000:000a", 443, 1); datacenters.put(datacenter.datacenterId, datacenter); datacenter = new Datacenter(); datacenter.datacenterId = 3; - datacenter.addAddressAndPort("149.154.175.100", 443); + datacenter.addAddressAndPort("149.154.175.100", 443, 0); + datacenter.addAddressAndPort("2001:b28:f23d:f003:0000:0000:0000:000a", 443, 1); datacenters.put(datacenter.datacenterId, datacenter); datacenter = new Datacenter(); datacenter.datacenterId = 4; - datacenter.addAddressAndPort("149.154.167.91", 443); + datacenter.addAddressAndPort("149.154.167.91", 443, 0); + datacenter.addAddressAndPort("2001:67c:4e8:f004:0000:0000:0000:000a", 443, 1); datacenters.put(datacenter.datacenterId, datacenter); datacenter = new Datacenter(); datacenter.datacenterId = 5; - datacenter.addAddressAndPort("149.154.171.5", 443); + datacenter.addAddressAndPort("149.154.171.5", 443, 0); + datacenter.addAddressAndPort("2001:b28:f23f:f005:0000:0000:0000:000a", 443, 1); datacenters.put(datacenter.datacenterId, datacenter); } } + @SuppressLint("NewApi") + protected static boolean useIpv6Address() { + if (BuildVars.DEBUG_VERSION && Build.VERSION.SDK_INT >= 19) { + try { + NetworkInterface networkInterface; + Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); + while (networkInterfaces.hasMoreElements()) { + networkInterface = networkInterfaces.nextElement(); + if (!networkInterface.isUp() || networkInterface.isLoopback() || networkInterface.getInterfaceAddresses().isEmpty()) { + continue; + } + FileLog.e("tmessages", "valid interface: " + networkInterface); + for (InterfaceAddress address : networkInterface.getInterfaceAddresses()) { + InetAddress inetAddress = address.getAddress(); + if (BuildVars.DEBUG_VERSION) { + FileLog.e("tmessages", "address: " + inetAddress.getHostAddress()); + } + if (inetAddress.isLinkLocalAddress() || inetAddress.isLoopbackAddress() || inetAddress.isMulticastAddress()) { + continue; + } + if (BuildVars.DEBUG_VERSION) { + FileLog.e("tmessages", "address is good"); + } + } + } + } catch (Throwable e) { + FileLog.e("tmessages", e); + } + } + if (Build.VERSION.SDK_INT < 50) { + return false; + } + try { + NetworkInterface networkInterface; + Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); + while (networkInterfaces.hasMoreElements()) { + networkInterface = networkInterfaces.nextElement(); + if (!networkInterface.isUp() || networkInterface.isLoopback()) { + continue; + } + boolean hasIpv4 = false; + boolean hasIpv6 = false; + for (InterfaceAddress address : networkInterface.getInterfaceAddresses()) { + InetAddress inetAddress = address.getAddress(); + if (inetAddress.isLinkLocalAddress() || inetAddress.isLoopbackAddress() || inetAddress.isMulticastAddress()) { + continue; + } + if (inetAddress instanceof Inet6Address) { + hasIpv6 = true; + } else if (inetAddress instanceof Inet4Address) { + hasIpv4 = true; + } + } + if (!hasIpv4 && hasIpv6) { + return true; + } + } + } catch (Throwable e) { + FileLog.e("tmessages", e); + } + + return false; + } + private void saveSession() { Utilities.stageQueue.postRunnable(new Runnable() { @Override @@ -625,7 +707,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } long generateMessageId() { - long messageId = (long)((((double)System.currentTimeMillis() + ((double)timeDifference) * 1000) * 4294967296.0) / 1000.0); + long messageId = (long) ((((double) System.currentTimeMillis() + ((double) timeDifference) * 1000) * 4294967296.0) / 1000.0); if (messageId <= lastOutgoingMessageId) { messageId = lastOutgoingMessageId + 1; } @@ -637,13 +719,14 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } long getTimeFromMsgId(long messageId) { - return (long)(messageId / 4294967296.0 * 1000); + return (long) (messageId / 4294967296.0 * 1000); } //================================================================================ // Requests manage //================================================================================ int lastClassGuid = 1; + public int generateClassGuid() { int guid = lastClassGuid++; requestsByGuids.put(guid, new ArrayList()); @@ -698,7 +781,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. HashMap ports = new HashMap<>(); addresses.add(ip_address); ports.put(ip_address, port); - exist.replaceAddressesAndPorts(addresses, ports); + exist.replaceAddressesAndPorts(addresses, ports, 0); exist.suspendConnections(); updateDcSettings(dc); } @@ -756,7 +839,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. if (updatingDcSettings) { return; } - updatingDcStartTime = (int)(System.currentTimeMillis() / 1000); + updatingDcStartTime = (int) (System.currentTimeMillis() / 1000); updatingDcSettings = true; TLRPC.TL_help_getConfig getConfig = new TLRPC.TL_help_getConfig(); @@ -767,12 +850,12 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. return; } if (error == null) { - TLRPC.TL_config config = (TLRPC.TL_config)response; + TLRPC.TL_config config = (TLRPC.TL_config) response; int updateIn = config.expires - getCurrentTime(); if (updateIn <= 0) { updateIn = 120; } - lastDcUpdateTime = (int)(System.currentTimeMillis() / 1000) - DC_UPDATE_TIME + updateIn; + lastDcUpdateTime = (int) (System.currentTimeMillis() / 1000) - DC_UPDATE_TIME + updateIn; ArrayList datacentersArr = new ArrayList<>(); HashMap datacenterMap = new HashMap<>(); for (TLRPC.TL_dcOption datacenterDesc : config.dc_options) { @@ -783,7 +866,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. datacentersArr.add(existing); datacenterMap.put(existing.datacenterId, existing); } - existing.addAddressAndPort(datacenterDesc.ip_address, datacenterDesc.port); + existing.addAddressAndPort(datacenterDesc.ip_address, datacenterDesc.port, datacenterDesc.flags); } if (!datacentersArr.isEmpty()) { @@ -792,7 +875,10 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. if (exist == null) { datacenters.put(datacenter.datacenterId, datacenter); } else { - exist.replaceAddressesAndPorts(datacenter.addresses, datacenter.ports); + exist.replaceAddressesAndPorts(datacenter.addressesIpv4, datacenter.ports, 0); + exist.replaceAddressesAndPorts(datacenter.addressesIpv6, datacenter.ports, 1); + exist.replaceAddressesAndPorts(datacenter.addressesIpv4Download, datacenter.ports, 2); + exist.replaceAddressesAndPorts(datacenter.addressesIpv6Download, datacenter.ports, 3); } if (datacenter.datacenterId == movingToDatacenterId) { movingToDatacenterId = DEFAULT_DATACENTER_ID; @@ -813,23 +899,20 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. private TLObject wrapInLayer(TLObject object, int datacenterId, RPCRequest request) { if (object.layer() > 0) { Datacenter datacenter = datacenterWithId(datacenterId); - if (datacenter == null || datacenter.lastInitVersion != currentAppVersion) { + if (datacenter == null || datacenter.lastInitVersion != BuildVars.BUILD_VERSION) { registerForPush(); request.initRequest = true; TLRPC.initConnection invoke = new TLRPC.initConnection(); invoke.query = object; invoke.api_id = BuildVars.APP_ID; try { - invoke.lang_code = LocaleController.getLocaleString(Locale.getDefault()); - invoke.device_model = Build.MANUFACTURER + Build.MODEL; - if (invoke.device_model == null) { - invoke.device_model = "Android unknown"; + invoke.lang_code = LocaleController.getLocaleString(LocaleController.getInstance().getSystemDefaultLocale()); + if (invoke.lang_code.length() == 0) { + invoke.lang_code = "en"; } + invoke.device_model = Build.MANUFACTURER + Build.MODEL; PackageInfo pInfo = ApplicationLoader.applicationContext.getPackageManager().getPackageInfo(ApplicationLoader.applicationContext.getPackageName(), 0); invoke.app_version = pInfo.versionName + " (" + pInfo.versionCode + ")"; - if (invoke.app_version == null) { - invoke.app_version = "App version unknown"; - } invoke.system_version = "SDK " + Build.VERSION.SDK_INT; } catch (Exception e) { FileLog.e("tmessages", e); @@ -962,7 +1045,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. public static boolean isNetworkOnline() { try { - ConnectivityManager cm = (ConnectivityManager)ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE); + ConnectivityManager cm = (ConnectivityManager) ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo netInfo = cm.getActiveNetworkInfo(); if (netInfo != null && (netInfo.isConnectedOrConnecting() || netInfo.isAvailable())) { return true; @@ -974,11 +1057,11 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. return true; } else { netInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI); - if(netInfo != null && netInfo.isConnectedOrConnecting()) { + if (netInfo != null && netInfo.isConnectedOrConnecting()) { return true; } } - } catch(Exception e) { + } catch (Exception e) { FileLog.e("tmessages", e); return true; } @@ -987,12 +1070,12 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. public static boolean isRoaming() { try { - ConnectivityManager cm = (ConnectivityManager)ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE); + ConnectivityManager cm = (ConnectivityManager) ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo netInfo = cm.getActiveNetworkInfo(); if (netInfo != null) { return netInfo.isRoaming(); } - } catch(Exception e) { + } catch (Exception e) { FileLog.e("tmessages", e); } return false; @@ -1000,19 +1083,19 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. public static boolean isConnectedToWiFi() { try { - ConnectivityManager cm = (ConnectivityManager)ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE); + ConnectivityManager cm = (ConnectivityManager) ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo netInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI); if (netInfo != null && netInfo.getState() == NetworkInfo.State.CONNECTED) { return true; } - } catch(Exception e) { + } catch (Exception e) { FileLog.e("tmessages", e); } return false; } public int getCurrentTime() { - return (int)(System.currentTimeMillis() / 1000) + timeDifference; + return (int) (System.currentTimeMillis() / 1000) + timeDifference; } public int getTimeDifference() { @@ -1033,7 +1116,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. genericConnection = defaultDatacenter.getGenericConnection(this); } - int currentTime = (int)(System.currentTimeMillis() / 1000); + int currentTime = (int) (System.currentTimeMillis() / 1000); for (int i = 0; i < runningRequests.size(); i++) { RPCRequest request = runningRequests.get(i); @@ -1062,7 +1145,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } Datacenter requestDatacenter = datacenterWithId(datacenterId); - if (!request.initRequest && requestDatacenter.lastInitVersion != currentAppVersion) { + if (!request.initRequest && requestDatacenter.lastInitVersion != BuildVars.BUILD_VERSION) { request.rpcRequest = wrapInLayer(request.rawRequest, requestDatacenter.datacenterId, request); ByteBufferDesc os = new ByteBufferDesc(true); request.rpcRequest.serializeToStream(os); @@ -1093,7 +1176,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. connection = requestDatacenter.getGenericConnection(this); } else if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) { connection = requestDatacenter.getDownloadConnection(this); - } else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0 ) { + } else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) { connection = requestDatacenter.getUploadConnection(this); } @@ -1203,7 +1286,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. continue; } if (System.currentTimeMillis() / 1000 - lastDestroySessionRequestTime > 2.0) { - lastDestroySessionRequestTime = (int)(System.currentTimeMillis() / 1000); + lastDestroySessionRequestTime = (int) (System.currentTimeMillis() / 1000); TLRPC.TL_destroy_session destroySession = new TLRPC.TL_destroy_session(); destroySession.session_id = it; destroyingSessions.add(it); @@ -1277,7 +1360,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } Datacenter requestDatacenter = datacenterWithId(datacenterId); - if (!request.initRequest && requestDatacenter.lastInitVersion != currentAppVersion) { + if (!request.initRequest && requestDatacenter.lastInitVersion != BuildVars.BUILD_VERSION) { request.rpcRequest = wrapInLayer(request.rawRequest, requestDatacenter.datacenterId, request); } @@ -1363,7 +1446,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. request.runningMessageId = messageId; request.runningMessageSeqNo = networkMessage.protoMessage.seqno; request.serializedLength = requestLength; - request.runningStartTime = (int)(System.currentTimeMillis() / 1000); + request.runningStartTime = (int) (System.currentTimeMillis() / 1000); request.transportChannelToken = connection.channelToken; if (request.requiresCompletion) { runningRequests.add(request); @@ -1486,7 +1569,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. boolean notFound = true; for (Action actor : actionQueue) { if (actor instanceof HandshakeAction) { - HandshakeAction eactor = (HandshakeAction)actor; + HandshakeAction eactor = (HandshakeAction) actor; if (eactor.datacenter.datacenterId == num) { notFound = false; break; @@ -1506,7 +1589,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. boolean notFound = true; for (Action actor : actionQueue) { if (actor instanceof ExportAuthorizationAction) { - ExportAuthorizationAction eactor = (ExportAuthorizationAction)actor; + ExportAuthorizationAction eactor = (ExportAuthorizationAction) actor; if (eactor.datacenter.datacenterId == num) { notFound = false; break; @@ -1554,7 +1637,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } ArrayList messages = new ArrayList<>(); - if(messageList != null) { + if (messageList != null) { messages.addAll(messageList); } @@ -1638,11 +1721,11 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. if (BuildVars.DEBUG_VERSION) { 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); + 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; + TLRPC.initConnection r = (TLRPC.initConnection) message.body; 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); + 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); } @@ -1652,7 +1735,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } long msg_time = getTimeFromMsgId(message.msg_id); - long currentTime = System.currentTimeMillis() + ((long)timeDifference) * 1000; + long currentTime = System.currentTimeMillis() + ((long) timeDifference) * 1000; if (msg_time < currentTime - 30000 || msg_time > currentTime + 25000) { FileLog.d("tmessages", "wrap in messages continaer"); @@ -1678,11 +1761,11 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. containerMessages.add(message); if (BuildVars.DEBUG_VERSION) { 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); + 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; + TLRPC.initConnection r = (TLRPC.initConnection) message.body; 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); + 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); } @@ -1722,11 +1805,11 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. if (quickAckId != null) { SerializedData data = new SerializedData(messageKeyFull); - quickAckId.add(data.readInt32() & 0x7fffffff); + quickAckId.add(data.readInt32(false) & 0x7fffffff); data.cleanup(); } - MessageKeyData keyData = Utilities.generateMessageKeyData(datacenter.authKey, messageKey, false); + MessageKeyData keyData = MessageKeyData.generateMessageKeyData(datacenter.authKey, messageKey, false); int zeroCount = 0; if (innerOs.limit() % 16 != 0) { @@ -1782,7 +1865,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. performRpc(getFutureSalts, new RPCRequest.RPCRequestDelegate() { @Override public void run(TLObject response, TLRPC.TL_error error) { - TLRPC.TL_futuresalts res = (TLRPC.TL_futuresalts)response; + TLRPC.TL_futuresalts res = (TLRPC.TL_futuresalts) response; if (error == null) { int currentTime = getCurrentTime(); datacenter.mergeServerSalts(currentTime, res.salts); @@ -1837,18 +1920,14 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. req.token = "" + pushSessionId; req.app_sandbox = false; try { - req.lang_code = LocaleController.getLocaleString(Locale.getDefault()); - req.device_model = Build.MANUFACTURER + Build.MODEL; - if (req.device_model == null) { - req.device_model = "Android unknown"; + req.lang_code = LocaleController.getLocaleString(LocaleController.getInstance().getSystemDefaultLocale()); + if (req.lang_code.length() == 0) { + req.lang_code = "en"; } + req.device_model = Build.MANUFACTURER + Build.MODEL; req.system_version = "SDK " + Build.VERSION.SDK_INT; PackageInfo pInfo = ApplicationLoader.applicationContext.getPackageManager().getPackageInfo(ApplicationLoader.applicationContext.getPackageName(), 0); req.app_version = pInfo.versionName + " (" + pInfo.versionCode + ")"; - if (req.app_version == null) { - req.app_version = "App version unknown"; - } - } catch (Exception e) { FileLog.e("tmessages", e); req.lang_code = "en"; @@ -1896,7 +1975,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. Datacenter datacenter = datacenterWithId(connection.getDatacenterId()); if (message instanceof TLRPC.TL_new_session_created) { - TLRPC.TL_new_session_created newSession = (TLRPC.TL_new_session_created)message; + TLRPC.TL_new_session_created newSession = (TLRPC.TL_new_session_created) message; if (!connection.isSessionProcessed(newSession.unique_id)) { FileLog.d("tmessages", "New session:"); @@ -1941,7 +2020,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. timeDifference = (int)((time - currentTime) / 1000 - currentPingTime / 2.0); }*/ - TLRPC.TL_msg_container messageContainer = (TLRPC.TL_msg_container)message; + TLRPC.TL_msg_container messageContainer = (TLRPC.TL_msg_container) message; for (TLRPC.TL_protoMessage innerMessage : messageContainer.messages) { long innerMessageId = innerMessage.msg_id; if (innerMessage.seqno % 2 != 0) { @@ -1989,7 +2068,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. sendingPushPing = false; } } else if (message instanceof TLRPC.TL_futuresalts) { - TLRPC.TL_futuresalts futureSalts = (TLRPC.TL_futuresalts)message; + TLRPC.TL_futuresalts futureSalts = (TLRPC.TL_futuresalts) message; long requestMid = futureSalts.req_msg_id; for (RPCRequest request : runningRequests) { if (request.respondsToMessageId(requestMid)) { @@ -2007,7 +2086,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } } } else if (message instanceof TLRPC.DestroySessionRes) { - TLRPC.DestroySessionRes res = (TLRPC.DestroySessionRes)message; + TLRPC.DestroySessionRes res = (TLRPC.DestroySessionRes) message; ArrayList lst = new ArrayList<>(); lst.addAll(sessionsToDestroy); destroyingSessions.remove(res.session_id); @@ -2019,18 +2098,18 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } } } else if (message instanceof TLRPC.TL_rpc_result) { - TLRPC.TL_rpc_result resultContainer = (TLRPC.TL_rpc_result)message; + TLRPC.TL_rpc_result resultContainer = (TLRPC.TL_rpc_result) message; long resultMid = resultContainer.req_msg_id; boolean ignoreResult = false; FileLog.d("tmessages", "object in rpc_result is " + resultContainer.result); if (resultContainer.result instanceof TLRPC.RpcError) { - String errorMessage = ((TLRPC.RpcError)resultContainer.result).error_message; - FileLog.e("tmessages", String.format("***** RPC error %d: %s", ((TLRPC.RpcError)resultContainer.result).error_code, errorMessage)); + String errorMessage = ((TLRPC.RpcError) resultContainer.result).error_message; + FileLog.e("tmessages", String.format("***** RPC error %d: %s", ((TLRPC.RpcError) resultContainer.result).error_code, errorMessage)); int migrateToDatacenterId = DEFAULT_DATACENTER_ID; - if (((TLRPC.RpcError)resultContainer.result).error_code == 303) { + if (((TLRPC.RpcError) resultContainer.result).error_code == 303) { ArrayList migrateErrors = new ArrayList<>(); migrateErrors.add("NETWORK_MIGRATE_"); migrateErrors.add("PHONE_MIGRATE_"); @@ -2079,21 +2158,16 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. boolean discardResponse = false; boolean isError = false; + boolean allowInitConnection = true; + if (request.completionBlock != null) { TLRPC.TL_error implicitError = null; if (resultContainer.result instanceof TLRPC.TL_gzip_packed) { - TLRPC.TL_gzip_packed packet = (TLRPC.TL_gzip_packed)resultContainer.result; - TLObject uncomressed = Utilities.decompress(packet.packed_data, request.rawRequest); - if (uncomressed == null) { - System.gc(); - uncomressed = Utilities.decompress(packet.packed_data, request.rawRequest); - } - if (uncomressed == null) { - throw new RuntimeException("failed to decomress responce for " + request.rawRequest); - } - resultContainer.result = uncomressed; + TLRPC.TL_gzip_packed packet = (TLRPC.TL_gzip_packed) resultContainer.result; + resultContainer.result = Utilities.decompress(packet.packed_data, request.rawRequest, true); } if (resultContainer.result instanceof TLRPC.RpcError) { + allowInitConnection = false; String errorMessage = ((TLRPC.RpcError) resultContainer.result).error_message; FileLog.e("tmessages", String.format("***** RPC error %d: %s", ((TLRPC.RpcError) resultContainer.result).error_code, errorMessage)); @@ -2106,6 +2180,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. request.runningMinStartTime = request.runningStartTime + delay; request.confirmed = false; } + request.serverFailureCount++; } else if (errorCode == 420) { if ((request.flags & RPCRequest.RPCRequestClassFailOnServerErrors) == 0) { @@ -2135,20 +2210,21 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. discardResponse = true; request.wait = true; - request.runningMinStartTime = (int)(System.currentTimeMillis() / 1000 + waitTime); + request.runningMinStartTime = (int) (System.currentTimeMillis() / 1000 + waitTime); request.confirmed = false; } } implicitError = new TLRPC.TL_error(); - implicitError.code = ((TLRPC.RpcError)resultContainer.result).error_code; - implicitError.text = ((TLRPC.RpcError)resultContainer.result).error_message; + implicitError.code = ((TLRPC.RpcError) resultContainer.result).error_code; + implicitError.text = ((TLRPC.RpcError) resultContainer.result).error_message; } else if (!(resultContainer.result instanceof TLRPC.TL_error)) { - if (request.rawRequest == null || resultContainer.result == null || !request.rawRequest.responseClass().isAssignableFrom(resultContainer.result.getClass())) { + if (request.rawRequest == null || resultContainer.result == null) { + allowInitConnection = false; if (request.rawRequest == null) { FileLog.e("tmessages", "rawRequest is null"); } else { - FileLog.e("tmessages", "***** RPC error: invalid response class " + resultContainer.result + " (" + request.rawRequest.responseClass() + " expected)"); + FileLog.e("tmessages", "***** RPC error: invalid response class " + resultContainer.result + " (for request " + request.rawRequest + ")"); } implicitError = new TLRPC.TL_error(); implicitError.code = -1000; @@ -2161,6 +2237,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. isError = true; request.completionBlock.run(null, implicitError != null ? implicitError : (TLRPC.TL_error) resultContainer.result); } else { + request.completionBlock.run(resultContainer.result, null); if (resultContainer.result instanceof TLRPC.updates_Difference) { pushMessagesReceived = true; AndroidUtilities.runOnUIThread(new Runnable() { @@ -2173,31 +2250,21 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } }); } - request.completionBlock.run(resultContainer.result, null); } } if (implicitError != null && implicitError.code == 401) { + allowInitConnection = false; isError = true; if (implicitError.text != null && implicitError.text.contains("SESSION_PASSWORD_NEEDED")) { - /*UserConfig.setWaitingForPasswordEnter(true); TODO - UserConfig.saveConfig(false); - if (UserConfig.isClientActivated()) { - discardResponse = true; - AndroidUtilities.runOnUIThread(new Runnable() { - @Override - public void run() { - NotificationCenter.getInstance().postNotificationName(NotificationCenter.needPasswordEnter); - } - }); - }*/ + //ignore this error } else if (datacenter.datacenterId == currentDatacenterId || datacenter.datacenterId == movingToDatacenterId) { if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0 && UserConfig.isClientActivated()) { UserConfig.clearConfig(); AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { - NotificationCenter.getInstance().postNotificationName(NotificationCenter.appDidLogout); + MessagesController.getInstance().performLogout(false); } }); } @@ -2214,9 +2281,9 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } if (!discardResponse) { - if (request.initRequest && !isError) { - if (datacenter.lastInitVersion != currentAppVersion) { - datacenter.lastInitVersion = currentAppVersion; + if (allowInitConnection && request.initRequest && !isError) { + if (datacenter.lastInitVersion != BuildVars.BUILD_VERSION) { + datacenter.lastInitVersion = BuildVars.BUILD_VERSION; saveSession(); FileLog.e("tmessages", "init connection completed"); } else { @@ -2254,7 +2321,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } else if (message instanceof TLRPC.TL_ping) { } else if (message instanceof TLRPC.TL_bad_msg_notification) { - TLRPC.TL_bad_msg_notification badMsgNotification = (TLRPC.TL_bad_msg_notification)message; + TLRPC.TL_bad_msg_notification badMsgNotification = (TLRPC.TL_bad_msg_notification) message; FileLog.e("tmessages", String.format("***** Bad message: %d", badMsgNotification.error_code)); @@ -2267,7 +2334,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. if (realId != 0) { long time = getTimeFromMsgId(messageId); long currentTime = System.currentTimeMillis(); - timeDifference = (int)((time - currentTime) / 1000 - currentPingTime / 2.0); + timeDifference = (int) ((time - currentTime) / 1000 - currentPingTime / 2.0); } datacenter.recreateSessions(); @@ -2283,7 +2350,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. if (messageId != 0) { long time = getTimeFromMsgId(messageId); long currentTime = System.currentTimeMillis(); - timeDifference = (int)((time - currentTime) / 1000 - currentPingTime / 2.0); + timeDifference = (int) ((time - currentTime) / 1000 - currentPingTime / 2.0); lastOutgoingMessageId = Math.max(messageId, lastOutgoingMessageId); } @@ -2316,7 +2383,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. processRequestQueue(RPCRequest.RPCRequestClassTransportMask, datacenter.datacenterId); } } else if (message instanceof TLRPC.MsgDetailedInfo) { - TLRPC.MsgDetailedInfo detailedInfo = (TLRPC.MsgDetailedInfo)message; + TLRPC.MsgDetailedInfo detailedInfo = (TLRPC.MsgDetailedInfo) message; boolean requestResend = false; boolean confirm = true; @@ -2327,8 +2394,8 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. if (request.completed) { break; } - if (request.lastResendTime == 0 || request.lastResendTime + 60 < (int)(System.currentTimeMillis() / 1000)) { - request.lastResendTime = (int)(System.currentTimeMillis() / 1000); + if (request.lastResendTime == 0 || request.lastResendTime + 60 < (int) (System.currentTimeMillis() / 1000)) { + request.lastResendTime = (int) (System.currentTimeMillis() / 1000); requestResend = true; } else { confirm = false; @@ -2356,9 +2423,11 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. connection.addMessageToConfirm(detailedInfo.answer_msg_id); } } else if (message instanceof TLRPC.TL_gzip_packed) { - TLRPC.TL_gzip_packed packet = (TLRPC.TL_gzip_packed)message; - TLObject result = Utilities.decompress(packet.packed_data, getRequestWithMessageId(messageId)); - processMessage(result, messageId, messageSeqNo, messageSalt, connection, innerMsgId, containerMessageId); + TLRPC.TL_gzip_packed packet = (TLRPC.TL_gzip_packed) message; + TLObject result = Utilities.decompress(packet.packed_data, getRequestWithMessageId(messageId), true); + if (result != null) { + processMessage(result, messageId, messageSeqNo, messageSalt, connection, innerMsgId, containerMessageId); + } } else if (message instanceof TLRPC.Updates) { if ((connection.transportRequestClass & RPCRequest.RPCRequestClassPush) != 0) { FileLog.e("tmessages", "received internal push"); @@ -2375,6 +2444,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. resumeNetworkInternal(); } else { pushMessagesReceived = true; + MessagesController.getInstance().processUpdates((TLRPC.Updates) message, false); AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { @@ -2384,7 +2454,6 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } } }); - MessagesController.getInstance().processUpdates((TLRPC.Updates) message, false); } } else { FileLog.e("tmessages", "***** Error: unknown message class " + message); @@ -2432,13 +2501,13 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } void generatePing(Datacenter datacenter, boolean push) { - TcpConnection connection = null; + TcpConnection connection; if (push) { connection = datacenter.pushConnection; } else { connection = datacenter.connection; } - if (connection != null && (push || !push && connection.channelToken != 0)) { + if (connection != null && (push || connection.channelToken != 0)) { ByteBufferDesc transportData = generatePingData(connection); if (transportData != null) { if (push) { @@ -2464,7 +2533,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } if (BuildVars.DEBUG_VERSION) { try { - ConnectivityManager cm = (ConnectivityManager)ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE); + ConnectivityManager cm = (ConnectivityManager) ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo[] networkInfos = cm.getAllNetworkInfo(); for (int a = 0; a < 2; a++) { if (a >= networkInfos.length) { @@ -2491,7 +2560,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. FileLog.e("tmessages", "push connection closed"); if (BuildVars.DEBUG_VERSION) { try { - ConnectivityManager cm = (ConnectivityManager)ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE); + ConnectivityManager cm = (ConnectivityManager) ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo[] networkInfos = cm.getAllNetworkInfo(); for (int a = 0; a < 2; a++) { if (a >= networkInfos.length) { @@ -2577,7 +2646,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } } if (length == 4) { - int error = data.readInt32(); + int error = data.readInt32(false); FileLog.e("tmessages", "mtproto error = " + error); connection.suspendConnection(true); connection.connect(); @@ -2585,22 +2654,20 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } Datacenter datacenter = datacenterWithId(connection.getDatacenterId()); - long keyId = data.readInt64(); + long keyId = data.readInt64(false); if (keyId == 0) { - long messageId = data.readInt64(); + long messageId = data.readInt64(false); if (connection.isMessageIdProcessed(messageId)) { finishUpdatingState(connection); return; } - int messageLength = data.readInt32(); - int constructor = data.readInt32(); + int messageLength = data.readInt32(false); - TLObject object = TLClassStore.Instance().TLdeserialize(data, constructor, getRequestWithMessageId(messageId)); + TLObject message = deserialize(getRequestWithMessageId(messageId), data, true); - processMessage(object, messageId, 0, 0, connection, 0, 0); - - if (object != null) { + if (message != null) { + processMessage(message, messageId, 0, 0, connection, 0, 0); connection.addProcessedMessageId(messageId); } } else { @@ -2612,13 +2679,13 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. return; } - byte[] messageKey = data.readData(16); - MessageKeyData keyData = Utilities.generateMessageKeyData(datacenter.authKey, messageKey, true); + byte[] messageKey = data.readData(16, false); + MessageKeyData keyData = MessageKeyData.generateMessageKeyData(datacenter.authKey, messageKey, true); Utilities.aesIgeEncryption(data.buffer, keyData.aesKey, keyData.aesIv, false, false, data.position(), length - 24); - long messageServerSalt = data.readInt64(); - long messageSessionId = data.readInt64(); + long messageServerSalt = data.readInt64(false); + long messageSessionId = data.readInt64(false); if (messageSessionId != connection.getSissionId()) { FileLog.e("tmessages", String.format("***** Error: invalid message session ID (%d instead of %d)", messageSessionId, connection.getSissionId())); @@ -2628,9 +2695,9 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. boolean doNotProcess = false; - long messageId = data.readInt64(); - int messageSeqNo = data.readInt32(); - int messageLength = data.readInt32(); + long messageId = data.readInt64(false); + int messageSeqNo = data.readInt32(false); + int messageLength = data.readInt32(false); if (connection.isMessageIdProcessed(messageId)) { doNotProcess = true; @@ -2654,12 +2721,8 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } if (!doNotProcess) { - int constructor = data.readInt32(); - TLObject message = TLClassStore.Instance().TLdeserialize(data, constructor, getRequestWithMessageId(messageId)); - - if (message == null) { - FileLog.e("tmessages", "***** Error parsing message: " + constructor); - } else { + TLObject message = deserialize(getRequestWithMessageId(messageId), data, true); + if (message != null) { FileLog.d("tmessages", "received object " + message); processMessage(message, messageId, messageSeqNo, messageServerSalt, connection, 0, 0); connection.addProcessedMessageId(messageId); @@ -2680,6 +2743,38 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } } + protected TLObject deserialize(TLObject request, AbsSerializedData data, boolean exception) { + int constructor = 0; + try { + constructor = data.readInt32(exception); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + TLObject message = null; + try { + message = TLClassStore.Instance().TLdeserialize(data, constructor, exception); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + if (message == null) { + if (request != null) { + try { + message = request.deserializeResponse(data, constructor, exception); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + if (message == null) { + FileLog.e("tmessages", String.format(Locale.US, "***** Error parsing message: %x", constructor)); + } + } else { + FileLog.d("tmessages", String.format(Locale.US, "***** Not found request to parse message: %x", constructor)); + } + } else if (message instanceof TLRPC.TL_rpc_result && ((TLRPC.TL_rpc_result) message).result == null) { + message = null; + } + return message; + } + public TLObject getRequestWithMessageId(long msgId) { for (RPCRequest request : runningRequests) { if (msgId == request.runningMessageId) { @@ -2712,7 +2807,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. @Override public void run(TLObject response, TLRPC.TL_error error) { if (error == null) { - movingAuthorization = (TLRPC.TL_auth_exportedAuthorization)response; + movingAuthorization = (TLRPC.TL_auth_exportedAuthorization) response; authorizeOnMovingDatacenter(); } else { Utilities.stageQueue.postRunnable(new Runnable() { @@ -2797,12 +2892,12 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. @Override public void ActionDidFinishExecution(final Action action, HashMap params) { if (action instanceof HandshakeAction) { - HandshakeAction eactor = (HandshakeAction)action; + HandshakeAction eactor = (HandshakeAction) action; eactor.datacenter.connection.delegate = this; saveSession(); if (eactor.datacenter.datacenterId == currentDatacenterId || eactor.datacenter.datacenterId == movingToDatacenterId) { - timeDifference = (Integer)params.get("timeDifference"); + timeDifference = (Integer) params.get("timeDifference"); eactor.datacenter.recreateSessions(); clearRequestsForRequestClass(RPCRequest.RPCRequestClassGeneric, eactor.datacenter); @@ -2811,7 +2906,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } processRequestQueue(RPCRequest.RPCRequestClassTransportMask, eactor.datacenter.datacenterId); } else if (action instanceof ExportAuthorizationAction) { - ExportAuthorizationAction eactor = (ExportAuthorizationAction)action; + ExportAuthorizationAction eactor = (ExportAuthorizationAction) action; Datacenter datacenter = eactor.datacenter; datacenter.authorized = true; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/Datacenter.java b/TMessagesProj/src/main/java/org/telegram/messenger/Datacenter.java index 61c63d2aa..cb28e751a 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/Datacenter.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/Datacenter.java @@ -1,9 +1,9 @@ /* - * This is the source code of Telegram for Android v. 1.3.2. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; @@ -17,10 +17,13 @@ import java.util.Comparator; import java.util.HashMap; public class Datacenter { - private static final int DATA_VERSION = 4; + private static final int DATA_VERSION = 5; public int datacenterId; - public ArrayList addresses = new ArrayList<>(); + public ArrayList addressesIpv4 = new ArrayList<>(); + public ArrayList addressesIpv6 = new ArrayList<>(); + public ArrayList addressesIpv4Download = new ArrayList<>(); + public ArrayList addressesIpv6Download = new ArrayList<>(); public HashMap ports = new HashMap<>(); public int[] defaultPorts = new int[] {-1, 80, -1, 443, -1, 443, -1, 80, -1, 443, -1}; public int[] defaultPorts8888 = new int[] {-1, 8888, -1, 443, -1, 8888, -1, 80, -1, 8888, -1}; @@ -29,8 +32,15 @@ public class Datacenter { public long authKeyId; public int lastInitVersion = 0; public int overridePort = -1; - private volatile int currentPortNum = 0; - private volatile int currentAddressNum = 0; + + private volatile int currentPortNumIpv4 = 0; + private volatile int currentAddressNumIpv4 = 0; + private volatile int currentPortNumIpv6 = 0; + private volatile int currentAddressNumIpv6 = 0; + private volatile int currentPortNumIpv4Download = 0; + private volatile int currentAddressNumIpv4Download = 0; + private volatile int currentPortNumIpv6Download = 0; + private volatile int currentAddressNumIpv6Download = 0; public TcpConnection connection; private TcpConnection downloadConnection; @@ -45,64 +55,84 @@ public class Datacenter { public Datacenter(SerializedData data, int version) { if (version == 0) { - datacenterId = data.readInt32(); - String address = data.readString(); - addresses.add(address); - int port = data.readInt32(); + datacenterId = data.readInt32(false); + String address = data.readString(false); + addressesIpv4.add(address); + int port = data.readInt32(false); ports.put(address, port); - int len = data.readInt32(); + int len = data.readInt32(false); if (len != 0) { - authKey = data.readData(len); + authKey = data.readData(len, false); } - len = data.readInt32(); + len = data.readInt32(false); if (len != 0) { - authKeyId = data.readInt64(); + authKeyId = data.readInt64(false); } - authorized = data.readInt32() != 0; - len = data.readInt32(); + authorized = data.readInt32(false) != 0; + len = data.readInt32(false); for (int a = 0; a < len; a++) { ServerSalt salt = new ServerSalt(); - salt.validSince = data.readInt32(); - salt.validUntil = data.readInt32(); - salt.value = data.readInt64(); + salt.validSince = data.readInt32(false); + salt.validUntil = data.readInt32(false); + salt.value = data.readInt64(false); if (authServerSaltSet == null) { authServerSaltSet = new ArrayList<>(); } authServerSaltSet.add(salt); } } else if (version == 1) { - int currentVersion = data.readInt32(); - if (currentVersion == 2 || currentVersion == 3 || currentVersion == 4) { - datacenterId = data.readInt32(); + int currentVersion = data.readInt32(false); + if (currentVersion >= 2 && currentVersion <= 5) { + datacenterId = data.readInt32(false); if (currentVersion >= 3) { - lastInitVersion = data.readInt32(); + lastInitVersion = data.readInt32(false); } - int len = data.readInt32(); + int len = data.readInt32(false); for (int a = 0; a < len; a++) { - String address = data.readString(); - addresses.add(address); - ports.put(address, data.readInt32()); + String address = data.readString(false); + addressesIpv4.add(address); + ports.put(address, data.readInt32(false)); } - - len = data.readInt32(); - if (len != 0) { - authKey = data.readData(len); - } - if (currentVersion == 4) { - authKeyId = data.readInt64(); - } else { - len = data.readInt32(); - if (len != 0) { - authKeyId = data.readInt64(); + if (currentVersion >= 5) { + len = data.readInt32(false); + for (int a = 0; a < len; a++) { + String address = data.readString(false); + addressesIpv6.add(address); + ports.put(address, data.readInt32(false)); + } + len = data.readInt32(false); + for (int a = 0; a < len; a++) { + String address = data.readString(false); + addressesIpv4Download.add(address); + ports.put(address, data.readInt32(false)); + } + len = data.readInt32(false); + for (int a = 0; a < len; a++) { + String address = data.readString(false); + addressesIpv6Download.add(address); + ports.put(address, data.readInt32(false)); } } - authorized = data.readInt32() != 0; - len = data.readInt32(); + + len = data.readInt32(false); + if (len != 0) { + authKey = data.readData(len, false); + } + if (currentVersion >= 4) { + authKeyId = data.readInt64(false); + } else { + len = data.readInt32(false); + if (len != 0) { + authKeyId = data.readInt64(false); + } + } + authorized = data.readInt32(false) != 0; + len = data.readInt32(false); for (int a = 0; a < len; a++) { ServerSalt salt = new ServerSalt(); - salt.validSince = data.readInt32(); - salt.validUntil = data.readInt32(); - salt.value = data.readInt64(); + salt.validSince = data.readInt32(false); + salt.validUntil = data.readInt32(false); + salt.value = data.readInt64(false); if (authServerSaltSet == null) { authServerSaltSet = new ArrayList<>(); } @@ -116,26 +146,79 @@ public class Datacenter { } public void switchTo443Port() { - for (int a = 0; a < addresses.size(); a++) { - if (ports.get(addresses.get(a)) == 443) { - currentAddressNum = a; - currentPortNum = 0; + for (int a = 0; a < addressesIpv4.size(); a++) { + if (ports.get(addressesIpv4.get(a)) == 443) { + currentAddressNumIpv4 = a; + currentPortNumIpv4 = 0; + break; + } + } + for (int a = 0; a < addressesIpv6.size(); a++) { + if (ports.get(addressesIpv6.get(a)) == 443) { + currentAddressNumIpv6 = a; + currentPortNumIpv6 = 0; + break; + } + } + for (int a = 0; a < addressesIpv4Download.size(); a++) { + if (ports.get(addressesIpv4Download.get(a)) == 443) { + currentAddressNumIpv4Download = a; + currentPortNumIpv4Download = 0; + break; + } + } + for (int a = 0; a < addressesIpv6Download.size(); a++) { + if (ports.get(addressesIpv6Download.get(a)) == 443) { + currentAddressNumIpv6Download = a; + currentPortNumIpv6Download = 0; break; } } } - public String getCurrentAddress() { + public String getCurrentAddress(int flags) { + int currentAddressNum; + ArrayList addresses; + if ((flags & 2) != 0) { + if ((flags & 1) != 0) { + currentAddressNum = currentAddressNumIpv6Download; + addresses = addressesIpv6Download; + } else { + currentAddressNum = currentAddressNumIpv4Download; + addresses = addressesIpv4Download; + } + } else { + if ((flags & 1) != 0) { + currentAddressNum = currentAddressNumIpv6; + addresses = addressesIpv6; + } else { + currentAddressNum = currentAddressNumIpv4; + addresses = addressesIpv4; + } + } if (addresses.isEmpty()) { return null; } if (currentAddressNum >= addresses.size()) { currentAddressNum = 0; + if ((flags & 2) != 0) { + if ((flags & 1) != 0) { + currentAddressNumIpv6Download = currentAddressNum; + } else { + currentAddressNumIpv4Download = currentAddressNum; + } + } else { + if ((flags & 1) != 0) { + currentAddressNumIpv6 = currentAddressNum; + } else { + currentAddressNumIpv4 = currentAddressNum; + } + } } return addresses.get(currentAddressNum); } - public int getCurrentPort() { + public int getCurrentPort(int flags) { if (ports.isEmpty()) { return overridePort == -1 ? 443 : overridePort; } @@ -146,21 +229,64 @@ public class Datacenter { portsArray = defaultPorts8888; } + int currentPortNum; + ArrayList addresses; + if ((flags & 2) != 0) { + if ((flags & 1) != 0) { + currentPortNum = currentPortNumIpv6Download; + } else { + currentPortNum = currentPortNumIpv4Download; + } + } else { + if ((flags & 1) != 0) { + currentPortNum = currentPortNumIpv6; + } else { + currentPortNum = currentPortNumIpv4; + } + } + if (currentPortNum >= defaultPorts.length) { currentPortNum = 0; + if ((flags & 2) != 0) { + if ((flags & 1) != 0) { + currentPortNumIpv6Download = currentPortNum; + } else { + currentPortNumIpv4Download = currentPortNum; + } + } else { + if ((flags & 1) != 0) { + currentPortNumIpv6 = currentPortNum; + } else { + currentPortNumIpv4 = currentPortNum; + } + } } int port = portsArray[currentPortNum]; if (port == -1) { if (overridePort != -1) { return overridePort; } - String address = getCurrentAddress(); + String address = getCurrentAddress(flags); return ports.get(address); } return port; } - public void addAddressAndPort(String address, int port) { + public void addAddressAndPort(String address, int port, int flags) { + ArrayList addresses; + if ((flags & 2) != 0) { + if ((flags & 1) != 0) { + addresses = addressesIpv6Download; + } else { + addresses = addressesIpv4Download; + } + } else { + if ((flags & 1) != 0) { + addresses = addressesIpv6; + } else { + addresses = addressesIpv4; + } + } if (addresses.contains(address)) { return; } @@ -168,7 +294,31 @@ public class Datacenter { ports.put(address, port); } - public void nextAddressOrPort() { + public void nextAddressOrPort(int flags) { + int currentPortNum; + int currentAddressNum; + ArrayList addresses; + if ((flags & 2) != 0) { + if ((flags & 1) != 0) { + currentPortNum = currentPortNumIpv6Download; + currentAddressNum = currentAddressNumIpv6Download; + addresses = addressesIpv6Download; + } else { + currentPortNum = currentPortNumIpv4Download; + currentAddressNum = currentAddressNumIpv4Download; + addresses = addressesIpv4Download; + } + } else { + if ((flags & 1) != 0) { + currentPortNum = currentPortNumIpv6; + currentAddressNum = currentAddressNumIpv6; + addresses = addressesIpv6; + } else { + currentPortNum = currentPortNumIpv4; + currentAddressNum = currentAddressNumIpv4; + addresses = addressesIpv4; + } + } if (currentPortNum + 1 < defaultPorts.length) { currentPortNum++; } else { @@ -179,6 +329,23 @@ public class Datacenter { } currentPortNum = 0; } + if ((flags & 2) != 0) { + if ((flags & 1) != 0) { + currentPortNumIpv6Download = currentPortNum; + currentAddressNumIpv6Download = currentAddressNum; + } else { + currentPortNumIpv4Download = currentPortNum; + currentAddressNumIpv4Download = currentAddressNum; + } + } else { + if ((flags & 1) != 0) { + currentPortNumIpv6 = currentPortNum; + currentAddressNumIpv6 = currentAddressNum; + } else { + currentPortNumIpv4 = currentPortNum; + currentAddressNumIpv4 = currentAddressNum; + } + } } public void storeCurrentAddressAndPortNum() { @@ -187,8 +354,14 @@ public class Datacenter { public void run() { SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("dataconfig", Context.MODE_PRIVATE); SharedPreferences.Editor editor = preferences.edit(); - editor.putInt("dc" + datacenterId + "port", currentPortNum); - editor.putInt("dc" + datacenterId + "address", currentAddressNum); + editor.putInt("dc" + datacenterId + "port", currentPortNumIpv4); + editor.putInt("dc" + datacenterId + "address", currentAddressNumIpv4); + editor.putInt("dc" + datacenterId + "port6", currentPortNumIpv6); + editor.putInt("dc" + datacenterId + "address6", currentAddressNumIpv6); + editor.putInt("dc" + datacenterId + "portD", currentPortNumIpv4Download); + editor.putInt("dc" + datacenterId + "addressD", currentAddressNumIpv4Download); + editor.putInt("dc" + datacenterId + "port6D", currentPortNumIpv6Download); + editor.putInt("dc" + datacenterId + "address6D", currentAddressNumIpv6Download); editor.commit(); } }); @@ -196,21 +369,71 @@ public class Datacenter { private void readCurrentAddressAndPortNum() { SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("dataconfig", Context.MODE_PRIVATE); - currentPortNum = preferences.getInt("dc" + datacenterId + "port", 0); - currentAddressNum = preferences.getInt("dc" + datacenterId + "address", 0); + currentPortNumIpv4 = preferences.getInt("dc" + datacenterId + "port", 0); + currentAddressNumIpv4 = preferences.getInt("dc" + datacenterId + "address", 0); + currentPortNumIpv6 = preferences.getInt("dc" + datacenterId + "port6", 0); + currentAddressNumIpv6 = preferences.getInt("dc" + datacenterId + "address6", 0); + currentPortNumIpv4Download = preferences.getInt("dc" + datacenterId + "portD", 0); + currentAddressNumIpv4Download = preferences.getInt("dc" + datacenterId + "addressD", 0); + currentPortNumIpv6Download = preferences.getInt("dc" + datacenterId + "port6D", 0); + currentAddressNumIpv6Download = preferences.getInt("dc" + datacenterId + "address6D", 0); } - public void replaceAddressesAndPorts(ArrayList newAddresses, HashMap newPorts) { - addresses = newAddresses; - ports = newPorts; + public void replaceAddressesAndPorts(ArrayList newAddresses, HashMap newPorts, int flags) { + ArrayList addresses; + if ((flags & 2) != 0) { + if ((flags & 1) != 0) { + addresses = addressesIpv6Download; + } else { + addresses = addressesIpv4Download; + } + } else { + if ((flags & 1) != 0) { + addresses = addressesIpv6; + } else { + addresses = addressesIpv4; + } + } + for (String address : addresses) { + ports.remove(address); + } + if ((flags & 2) != 0) { + if ((flags & 1) != 0) { + addressesIpv6Download = newAddresses; + } else { + addressesIpv4Download = newAddresses; + } + } else { + if ((flags & 1) != 0) { + addressesIpv6 = newAddresses; + } else { + addressesIpv4 = newAddresses; + } + } + ports.putAll(newPorts); } public void SerializeToStream(SerializedData stream) { stream.writeInt32(DATA_VERSION); stream.writeInt32(datacenterId); stream.writeInt32(lastInitVersion); - stream.writeInt32(addresses.size()); - for (String address : addresses) { + stream.writeInt32(addressesIpv4.size()); + for (String address : addressesIpv4) { + stream.writeString(address); + stream.writeInt32(ports.get(address)); + } + stream.writeInt32(addressesIpv6.size()); + for (String address : addressesIpv6) { + stream.writeString(address); + stream.writeInt32(ports.get(address)); + } + stream.writeInt32(addressesIpv4Download.size()); + for (String address : addressesIpv4Download) { + stream.writeString(address); + stream.writeInt32(ports.get(address)); + } + stream.writeInt32(addressesIpv6Download.size()); + for (String address : addressesIpv6Download) { stream.writeString(address); stream.writeInt32(ports.get(address)); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueue.java b/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueue.java index 2c68fdf6e..529cbdbc5 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueue.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueue.java @@ -1,9 +1,9 @@ /* - * This is the source code of Telegram for Android v. 1.3.2. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ExportAuthorizationAction.java b/TMessagesProj/src/main/java/org/telegram/messenger/ExportAuthorizationAction.java index 51367696d..603032c29 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ExportAuthorizationAction.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ExportAuthorizationAction.java @@ -1,9 +1,9 @@ /* - * This is the source code of Telegram for Android v. 1.3.2. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java index 62f217442..2a006d37b 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java @@ -1,9 +1,9 @@ /* - * This is the source code of Telegram for Android v. 1.3.2. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; @@ -60,7 +60,7 @@ public class FileLoadOperation { void didChangedLoadProgress(FileLoadOperation operation, float progress); } - public FileLoadOperation(TLRPC.FileLocation photoLocation, int size) { + public FileLoadOperation(TLRPC.FileLocation photoLocation, String extension, int size) { if (photoLocation instanceof TLRPC.TL_fileEncryptedLocation) { location = new TLRPC.TL_inputEncryptedFileLocation(); location.id = photoLocation.volume_id; @@ -79,10 +79,7 @@ public class FileLoadOperation { datacenter_id = photoLocation.dc_id; } totalBytesCount = size; - ext = photoLocation.ext; - if (ext == null) { - ext = "jpg"; - } + ext = extension != null ? extension : "jpg"; } public FileLoadOperation(TLRPC.Video videoLocation) { @@ -140,7 +137,7 @@ public class FileLoadOperation { } totalBytesCount = documentLocation.size; ext = FileLoader.getDocumentFileName(documentLocation); - int idx = -1; + int idx; if (ext == null || (idx = ext.lastIndexOf(".")) == -1) { ext = ""; } else { @@ -179,8 +176,8 @@ public class FileLoadOperation { return; } Long mediaId = null; - String fileNameFinal = null; - String fileNameTemp = null; + String fileNameFinal; + String fileNameTemp; String fileNameIv = null; if (location.volume_id != 0 && location.local_id != 0) { fileNameTemp = location.volume_id + "_" + location.local_id + "_temp." + ext; @@ -219,7 +216,6 @@ public class FileLoadOperation { cacheFileFinal = new File(storePath, fileNameFinal); boolean exist = cacheFileFinal.exists(); if (exist && totalBytesCount != 0 && totalBytesCount != cacheFileFinal.length()) { - exist = false; cacheFileFinal.delete(); } @@ -388,7 +384,6 @@ public class FileLoadOperation { processRequestResult(delayedRequestInfo, null); delayedRequestInfo.response.disableFree = false; delayedRequestInfo.response.freeResources(); - delayedRequestInfo = null; break; } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java index af3ae922c..2860c3cc9 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java @@ -1,9 +1,9 @@ /* - * This is the source code of Telegram for Android v. 1.3.2. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; @@ -21,10 +21,15 @@ public class FileLoader { public interface FileLoaderDelegate { void fileUploadProgressChanged(String location, float progress, boolean isEncrypted); - void fileDidUploaded(String location, TLRPC.InputFile inputFile, TLRPC.InputEncryptedFile inputEncryptedFile); + + void fileDidUploaded(String location, TLRPC.InputFile inputFile, TLRPC.InputEncryptedFile inputEncryptedFile, byte[] key, byte[] iv); + void fileDidFailedUpload(String location, boolean isEncrypted); + void fileDidLoaded(String location, File finalFile, int type); + void fileDidFailedLoad(String location, int state); + void fileLoadProgressChanged(String location, float progress); } @@ -56,6 +61,7 @@ public class FileLoader { private int currentUploadSmallOperationsCount = 0; private static volatile FileLoader Instance = null; + public static FileLoader getInstance() { FileLoader localInstance = Instance; if (localInstance == null) { @@ -92,7 +98,7 @@ public class FileLoader { fileLoaderQueue.postRunnable(new Runnable() { @Override public void run() { - FileUploadOperation operation = null; + FileUploadOperation operation; if (!enc) { operation = uploadOperationPaths.get(location); } else { @@ -112,7 +118,7 @@ public class FileLoader { fileLoaderQueue.postRunnable(new Runnable() { @Override public void run() { - FileUploadOperation operation = null; + FileUploadOperation operation; if (encrypted) { operation = uploadOperationPathsEnc.get(location); } else { @@ -163,7 +169,7 @@ public class FileLoader { } operation.delegate = new FileUploadOperation.FileUploadOperationDelegate() { @Override - public void didFinishUploadingFile(FileUploadOperation operation, final TLRPC.InputFile inputFile, final TLRPC.InputEncryptedFile inputEncryptedFile) { + public void didFinishUploadingFile(FileUploadOperation operation, final TLRPC.InputFile inputFile, final TLRPC.InputEncryptedFile inputEncryptedFile, final byte[] key, final byte[] iv) { fileLoaderQueue.postRunnable(new Runnable() { @Override public void run() { @@ -192,7 +198,7 @@ public class FileLoader { } } if (delegate != null) { - delegate.fileDidUploaded(location, inputFile, inputEncryptedFile); + delegate.fileDidUploaded(location, inputFile, inputEncryptedFile, key, iv); } } }); @@ -261,26 +267,26 @@ public class FileLoader { } public void cancelLoadFile(TLRPC.Video video) { - cancelLoadFile(video, null, null, null); + cancelLoadFile(video, null, null, null, null); } public void cancelLoadFile(TLRPC.Document document) { - cancelLoadFile(null, document, null, null); + cancelLoadFile(null, document, null, null, null); } public void cancelLoadFile(TLRPC.Audio audio) { - cancelLoadFile(null, null, audio, null); + cancelLoadFile(null, null, audio, null, null); } public void cancelLoadFile(TLRPC.PhotoSize photo) { - cancelLoadFile(null, null, null, photo.location); + cancelLoadFile(null, null, null, photo.location, null); } - public void cancelLoadFile(TLRPC.FileLocation location) { - cancelLoadFile(null, null, null, location); + public void cancelLoadFile(TLRPC.FileLocation location, String ext) { + cancelLoadFile(null, null, null, location, ext); } - private void cancelLoadFile(final TLRPC.Video video, final TLRPC.Document document, final TLRPC.Audio audio, final TLRPC.FileLocation location) { + private void cancelLoadFile(final TLRPC.Video video, final TLRPC.Document document, final TLRPC.Audio audio, final TLRPC.FileLocation location, final String locationExt) { if (video == null && location == null && document == null && audio == null) { return; } @@ -291,7 +297,7 @@ public class FileLoader { if (video != null) { fileName = getAttachFileName(video); } else if (location != null) { - fileName = getAttachFileName(location); + fileName = getAttachFileName(location, locationExt); } else if (document != null) { fileName = getAttachFileName(document); } else if (audio != null) { @@ -335,26 +341,26 @@ public class FileLoader { } public void loadFile(TLRPC.Video video, boolean force) { - loadFile(video, null, null, null, 0, force, video != null && video.key != null); + loadFile(video, null, null, null, null, 0, force, video != null && video.key != null); } - public void loadFile(TLRPC.PhotoSize photo, boolean cacheOnly) { - loadFile(null, null, null, photo.location, photo.size, false, cacheOnly || (photo != null && photo.size == 0 || photo.location.key != null)); + public void loadFile(TLRPC.PhotoSize photo, String ext, boolean cacheOnly) { + loadFile(null, null, null, photo.location, ext, photo.size, false, cacheOnly || (photo != null && photo.size == 0 || photo.location.key != null)); } public void loadFile(TLRPC.Document document, boolean force, boolean cacheOnly) { - loadFile(null, document, null, null, 0, force, cacheOnly || document != null && document.key != null); + loadFile(null, document, null, null, null, 0, force, cacheOnly || document != null && document.key != null); } public void loadFile(TLRPC.Audio audio, boolean force) { - loadFile(null, null, audio, null, 0, false, audio != null && audio.key != null); + loadFile(null, null, audio, null, null, 0, false, audio != null && audio.key != null); } - public void loadFile(TLRPC.FileLocation location, int size, boolean cacheOnly) { - loadFile(null, null, null, location, size, true, cacheOnly || size == 0 || (location != null && location.key != null)); + public void loadFile(TLRPC.FileLocation location, String ext, int size, boolean cacheOnly) { + loadFile(null, null, null, location, ext, size, true, cacheOnly || size == 0 || (location != null && location.key != null)); } - private void loadFile(final TLRPC.Video video, final TLRPC.Document document, final TLRPC.Audio audio, final TLRPC.FileLocation location, final int locationSize, final boolean force, final boolean cacheOnly) { + private void loadFile(final TLRPC.Video video, final TLRPC.Document document, final TLRPC.Audio audio, final TLRPC.FileLocation location, final String locationExt, final int locationSize, final boolean force, final boolean cacheOnly) { fileLoaderQueue.postRunnable(new Runnable() { @Override public void run() { @@ -362,7 +368,7 @@ public class FileLoader { if (video != null) { fileName = getAttachFileName(video); } else if (location != null) { - fileName = getAttachFileName(location); + fileName = getAttachFileName(location, locationExt); } else if (document != null) { fileName = getAttachFileName(document); } else if (audio != null) { @@ -372,11 +378,11 @@ public class FileLoader { return; } - FileLoadOperation operation = null; + FileLoadOperation operation; operation = loadOperationPaths.get(fileName); if (operation != null) { if (force) { - LinkedList downloadQueue = null; + LinkedList downloadQueue; if (audio != null) { downloadQueue = audioLoadOperationQueue; } else if (location != null) { @@ -404,7 +410,7 @@ public class FileLoader { operation = new FileLoadOperation(video); type = MEDIA_DIR_VIDEO; } else if (location != null) { - operation = new FileLoadOperation(location, locationSize); + operation = new FileLoadOperation(location, locationExt, locationSize); type = MEDIA_DIR_IMAGE; } else if (document != null) { operation = new FileLoadOperation(document); @@ -489,7 +495,7 @@ public class FileLoader { @Override public void run() { loadOperationPaths.remove(arg1); - FileLoadOperation operation = null; + FileLoadOperation operation; if (audio != null) { currentAudioLoadOperationsCount--; if (!audioLoadOperationQueue.isEmpty()) { @@ -583,54 +589,66 @@ public class FileLoader { } public static File getPathToAttach(TLObject attach) { - return getPathToAttach(attach, false); + return getPathToAttach(attach, null, false); } public static File getPathToAttach(TLObject attach, boolean forceCache) { + return getPathToAttach(attach, null, forceCache); + } + + public static File getPathToAttach(TLObject attach, String ext, boolean forceCache) { File dir = null; - if (attach instanceof TLRPC.Video) { - TLRPC.Video video = (TLRPC.Video)attach; - if (forceCache || video.key != null) { - dir = getInstance().getDirectory(MEDIA_DIR_CACHE); - } else { - dir = getInstance().getDirectory(MEDIA_DIR_VIDEO); - } - } else if (attach instanceof TLRPC.Document) { - TLRPC.Document document = (TLRPC.Document)attach; - if (forceCache || document.key != null) { - dir = getInstance().getDirectory(MEDIA_DIR_CACHE); - } else { - dir = getInstance().getDirectory(MEDIA_DIR_DOCUMENT); - } - } else if (attach instanceof TLRPC.PhotoSize) { - TLRPC.PhotoSize photoSize = (TLRPC.PhotoSize)attach; - if (forceCache || photoSize.location == null || photoSize.location.key != null || photoSize.location.volume_id == Integer.MIN_VALUE && photoSize.location.local_id < 0) { - dir = getInstance().getDirectory(MEDIA_DIR_CACHE); - } else { - dir = getInstance().getDirectory(MEDIA_DIR_IMAGE); - } - } else if (attach instanceof TLRPC.Audio) { - TLRPC.Audio audio = (TLRPC.Audio)attach; - if (forceCache || audio.key != null) { - dir = getInstance().getDirectory(MEDIA_DIR_CACHE); - } else { - dir = getInstance().getDirectory(MEDIA_DIR_AUDIO); - } - } else if (attach instanceof TLRPC.FileLocation) { - TLRPC.FileLocation fileLocation = (TLRPC.FileLocation)attach; - if (forceCache || fileLocation.key != null || fileLocation.volume_id == Integer.MIN_VALUE && fileLocation.local_id < 0) { - dir = getInstance().getDirectory(MEDIA_DIR_CACHE); - } else { - dir = getInstance().getDirectory(MEDIA_DIR_IMAGE); + if (forceCache) { + dir = getInstance().getDirectory(MEDIA_DIR_CACHE); + } else { + if (attach instanceof TLRPC.Video) { + TLRPC.Video video = (TLRPC.Video) attach; + if (video.key != null) { + dir = getInstance().getDirectory(MEDIA_DIR_CACHE); + } else { + dir = getInstance().getDirectory(MEDIA_DIR_VIDEO); + } + } else if (attach instanceof TLRPC.Document) { + TLRPC.Document document = (TLRPC.Document) attach; + if (document.key != null) { + dir = getInstance().getDirectory(MEDIA_DIR_CACHE); + } else { + dir = getInstance().getDirectory(MEDIA_DIR_DOCUMENT); + } + } else if (attach instanceof TLRPC.PhotoSize) { + TLRPC.PhotoSize photoSize = (TLRPC.PhotoSize) attach; + if (photoSize.location == null || photoSize.location.key != null || photoSize.location.volume_id == Integer.MIN_VALUE && photoSize.location.local_id < 0) { + dir = getInstance().getDirectory(MEDIA_DIR_CACHE); + } else { + dir = getInstance().getDirectory(MEDIA_DIR_IMAGE); + } + } else if (attach instanceof TLRPC.Audio) { + TLRPC.Audio audio = (TLRPC.Audio) attach; + if (audio.key != null) { + dir = getInstance().getDirectory(MEDIA_DIR_CACHE); + } else { + dir = getInstance().getDirectory(MEDIA_DIR_AUDIO); + } + } else if (attach instanceof TLRPC.FileLocation) { + TLRPC.FileLocation fileLocation = (TLRPC.FileLocation) attach; + if (fileLocation.key != null || fileLocation.volume_id == Integer.MIN_VALUE && fileLocation.local_id < 0) { + dir = getInstance().getDirectory(MEDIA_DIR_CACHE); + } else { + dir = getInstance().getDirectory(MEDIA_DIR_IMAGE); + } } } if (dir == null) { return new File(""); } - return new File(dir, getAttachFileName(attach)); + return new File(dir, getAttachFileName(attach, ext)); } public static TLRPC.PhotoSize getClosestPhotoSizeWithSize(ArrayList sizes, int side) { + return getClosestPhotoSizeWithSize(sizes, side, false); + } + + public static TLRPC.PhotoSize getClosestPhotoSizeWithSize(ArrayList sizes, int side, boolean byMinSide) { if (sizes == null || sizes.isEmpty()) { return null; } @@ -640,10 +658,18 @@ public class FileLoader { if (obj == null) { continue; } - int currentSide = obj.w >= obj.h ? obj.w : obj.h; - if (closestObject == null || side > 100 && closestObject.location != null && closestObject.location.dc_id == Integer.MIN_VALUE || obj instanceof TLRPC.TL_photoCachedSize || currentSide <= side && lastSide < currentSide) { - closestObject = obj; - lastSide = currentSide; + if (byMinSide) { + int currentSide = obj.h >= obj.w ? obj.w : obj.h; + if (closestObject == null || side > 100 && closestObject.location != null && closestObject.location.dc_id == Integer.MIN_VALUE || obj instanceof TLRPC.TL_photoCachedSize || side > lastSide && lastSide < currentSide) { + closestObject = obj; + lastSide = currentSide; + } + } else { + int currentSide = obj.w >= obj.h ? obj.w : obj.h; + if (closestObject == null || side > 100 && closestObject.location != null && closestObject.location.dc_id == Integer.MIN_VALUE || obj instanceof TLRPC.TL_photoCachedSize || currentSide <= side && lastSide < currentSide) { + closestObject = obj; + lastSide = currentSide; + } } } return closestObject; @@ -651,6 +677,9 @@ public class FileLoader { public static String getDocumentFileName(TLRPC.Document document) { if (document != null) { + if (document.file_name != null) { + return document.file_name; + } for (TLRPC.DocumentAttribute documentAttribute : document.attributes) { if (documentAttribute instanceof TLRPC.TL_documentAttributeFilename) { return documentAttribute.file_name; @@ -661,35 +690,39 @@ public class FileLoader { } public static String getAttachFileName(TLObject attach) { + return getAttachFileName(attach, null); + } + + public static String getAttachFileName(TLObject attach, String ext) { if (attach instanceof TLRPC.Video) { - TLRPC.Video video = (TLRPC.Video)attach; - return video.dc_id + "_" + video.id + ".mp4"; + TLRPC.Video video = (TLRPC.Video) attach; + return video.dc_id + "_" + video.id + "." + (ext != null ? ext : "mp4"); } else if (attach instanceof TLRPC.Document) { - TLRPC.Document document = (TLRPC.Document)attach; - String ext = getDocumentFileName(document); - int idx = -1; - if (ext == null || (idx = ext.lastIndexOf(".")) == -1) { - ext = ""; + TLRPC.Document document = (TLRPC.Document) attach; + String docExt = getDocumentFileName(document); + int idx; + if (docExt == null || (idx = docExt.lastIndexOf(".")) == -1) { + docExt = ""; } else { - ext = ext.substring(idx); + docExt = docExt.substring(idx); } - if (ext.length() > 1) { - return document.dc_id + "_" + document.id + ext; + if (docExt.length() > 1) { + return document.dc_id + "_" + document.id + docExt; } else { return document.dc_id + "_" + document.id; } } else if (attach instanceof TLRPC.PhotoSize) { - TLRPC.PhotoSize photo = (TLRPC.PhotoSize)attach; + TLRPC.PhotoSize photo = (TLRPC.PhotoSize) attach; if (photo.location == null) { return ""; } - return photo.location.volume_id + "_" + photo.location.local_id + "." + (photo.location.ext != null ? photo.location.ext : "jpg"); + return photo.location.volume_id + "_" + photo.location.local_id + "." + (ext != null ? ext : "jpg"); } else if (attach instanceof TLRPC.Audio) { - TLRPC.Audio audio = (TLRPC.Audio)attach; - return audio.dc_id + "_" + audio.id + ".ogg"; + TLRPC.Audio audio = (TLRPC.Audio) attach; + return audio.dc_id + "_" + audio.id + "." + (ext != null ? ext : "ogg"); } else if (attach instanceof TLRPC.FileLocation) { - TLRPC.FileLocation location = (TLRPC.FileLocation)attach; - return location.volume_id + "_" + location.local_id + "." + (location.ext != null ? location.ext : "jpg"); + TLRPC.FileLocation location = (TLRPC.FileLocation) attach; + return location.volume_id + "_" + location.local_id + "." + (ext != null ? ext : "jpg"); } return ""; } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLog.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLog.java index e72408acc..b54bff7a4 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLog.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLog.java @@ -1,14 +1,13 @@ /* - * This is the source code of Telegram for Android v. 1.3.2. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; -import android.net.Uri; import android.util.Log; import org.telegram.android.time.FastDateFormat; @@ -16,7 +15,6 @@ import org.telegram.android.time.FastDateFormat; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStreamWriter; -import java.util.ArrayList; import java.util.Locale; public class FileLog { @@ -50,14 +48,8 @@ public class FileLog { return; } File dir = new File(sdCard.getAbsolutePath() + "/logs"); - if (dir == null) { - return; - } dir.mkdirs(); currentFile = new File(dir, dateFormat.format(System.currentTimeMillis()) + ".txt"); - if (currentFile == null) { - return; - } } catch (Exception e) { e.printStackTrace(); } @@ -181,7 +173,6 @@ public class FileLog { } public static void cleanupLogs() { - ArrayList uris = new ArrayList<>(); File sdCard = ApplicationLoader.applicationContext.getExternalFilesDir(null); File dir = new File (sdCard.getAbsolutePath() + "/logs"); File[] files = dir.listFiles(); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileUploadOperation.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileUploadOperation.java index 788a202a4..1faeda79f 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileUploadOperation.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileUploadOperation.java @@ -1,9 +1,9 @@ /* - * This is the source code of Telegram for Android v. 1.3.2. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; @@ -46,7 +46,7 @@ public class FileUploadOperation { private boolean started = false; public interface FileUploadOperationDelegate { - void didFinishUploadingFile(FileUploadOperation operation, TLRPC.InputFile inputFile, TLRPC.InputEncryptedFile inputEncryptedFile); + void didFinishUploadingFile(FileUploadOperation operation, TLRPC.InputFile inputFile, TLRPC.InputEncryptedFile inputEncryptedFile, byte[] key, byte[] iv); void didFailedUploadingFile(FileUploadOperation operation); void didChangedUploadProgress(FileUploadOperation operation, float progress); } @@ -190,8 +190,12 @@ public class FileUploadOperation { if (ivString != null && keyString != null) { key = Utilities.hexToBytes(keyString); iv = Utilities.hexToBytes(ivString); - ivChange = new byte[32]; - System.arraycopy(iv, 0, ivChange, 0, 32); + if (key != null && iv != null && key.length == 32 && iv.length == 32) { + ivChange = new byte[32]; + System.arraycopy(iv, 0, ivChange, 0, 32); + } else { + rewrite = true; + } } else { rewrite = true; } @@ -234,6 +238,11 @@ public class FileUploadOperation { String ivcString = preferences.getString(fileKey + "_ivc", null); if (ivcString != null) { ivChange = Utilities.hexToBytes(ivcString); + if (ivChange == null || ivChange.length != 32) { + rewrite = true; + currentUploaded = 0; + currentPartNum = 0; + } } else { rewrite = true; currentUploaded = 0; @@ -369,7 +378,7 @@ public class FileUploadOperation { result.parts = currentPartNum; result.id = currentFileId; result.name = uploadingFilePath.substring(uploadingFilePath.lastIndexOf("/") + 1); - delegate.didFinishUploadingFile(FileUploadOperation.this, result, null); + delegate.didFinishUploadingFile(FileUploadOperation.this, result, null, null, null); cleanup(); } else { TLRPC.InputEncryptedFile result; @@ -382,9 +391,7 @@ public class FileUploadOperation { result.parts = currentPartNum; result.id = currentFileId; result.key_fingerprint = fingerprint; - result.iv = iv; - result.key = key; - delegate.didFinishUploadingFile(FileUploadOperation.this, null, result); + delegate.didFinishUploadingFile(FileUploadOperation.this, null, result, key, iv); cleanup(); } } else { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/HandshakeAction.java b/TMessagesProj/src/main/java/org/telegram/messenger/HandshakeAction.java index dc5f426d1..eb225c09c 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/HandshakeAction.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/HandshakeAction.java @@ -1,9 +1,9 @@ /* - * This is the source code of Telegram for Android v. 1.3.2. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; @@ -340,7 +340,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti Utilities.aesIgeEncryption(answerWithHash.buffer, tmpAesKey.toByteArray(), tmpAesIv.toByteArray(), false, false, 0, serverDhParams.encrypted_answer.length); byte[] answerHash = new byte[20]; - answerWithHash.readRaw(answerHash); + answerWithHash.readRaw(answerHash, false); boolean hashVerified = false; for (int i = 0; i < 16; i++) { @@ -358,8 +358,8 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti return; } - int constructor = answerWithHash.readInt32(); - TLRPC.TL_server_DH_inner_data dhInnerData = (TLRPC.TL_server_DH_inner_data)TLClassStore.Instance().TLdeserialize(answerWithHash, constructor); + int constructor = answerWithHash.readInt32(false); + TLRPC.TL_server_DH_inner_data dhInnerData = TLRPC.TL_server_DH_inner_data.TLdeserialize(answerWithHash, constructor, false); BuffersStorage.getInstance().reuseFreeBuffer(answerWithHash); if (!(dhInnerData instanceof TLRPC.TL_server_DH_inner_data)) { @@ -627,17 +627,17 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti @Override public void tcpConnectionReceivedData(TcpConnection connection, ByteBufferDesc data, int length) { - long keyId = data.readInt64(); + long keyId = data.readInt64(false); if (keyId == 0) { - long messageId = data.readInt64(); + long messageId = data.readInt64(false); if (processedMessageIds.contains(messageId)) { FileLog.d("tmessages", String.format("===== Duplicate message id %d received, ignoring", messageId)); return; } - int messageLength = data.readInt32(); + int messageLength = data.readInt32(false); - int constructor = data.readInt32(); - TLObject object = TLClassStore.Instance().TLdeserialize(data, constructor); + int constructor = data.readInt32(false); + TLObject object = TLClassStore.Instance().TLdeserialize(data, constructor, false); if (object != null) { processedMessageIds.add(messageId); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessageKeyData.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessageKeyData.java index 6aef7a298..669f3a029 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessageKeyData.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessageKeyData.java @@ -1,14 +1,68 @@ /* - * This is the source code of Telegram for Android v. 1.3.2. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; public class MessageKeyData { + public byte[] aesKey; public byte[] aesIv; + + public static MessageKeyData generateMessageKeyData(byte[] authKey, byte[] messageKey, boolean incoming) { + MessageKeyData keyData = new MessageKeyData(); + if (authKey == null || authKey.length == 0) { + keyData.aesIv = null; + keyData.aesKey = null; + return keyData; + } + + int x = incoming ? 8 : 0; + + SerializedData data = new SerializedData(); + data.writeRaw(messageKey); + data.writeRaw(authKey, x, 32); + byte[] sha1_a = Utilities.computeSHA1(data.toByteArray()); + data.cleanup(); + + data = new SerializedData(); + data.writeRaw(authKey, 32 + x, 16); + data.writeRaw(messageKey); + data.writeRaw(authKey, 48 + x, 16); + byte[] sha1_b = Utilities.computeSHA1(data.toByteArray()); + data.cleanup(); + + data = new SerializedData(); + data.writeRaw(authKey, 64 + x, 32); + data.writeRaw(messageKey); + byte[] sha1_c = Utilities.computeSHA1(data.toByteArray()); + data.cleanup(); + + data = new SerializedData(); + data.writeRaw(messageKey); + data.writeRaw(authKey, 96 + x, 32); + byte[] sha1_d = Utilities.computeSHA1(data.toByteArray()); + data.cleanup(); + + data = new SerializedData(); + data.writeRaw(sha1_a, 0, 8); + data.writeRaw(sha1_b, 8, 12); + data.writeRaw(sha1_c, 4, 12); + keyData.aesKey = data.toByteArray(); + data.cleanup(); + + data = new SerializedData(); + data.writeRaw(sha1_a, 8, 12); + data.writeRaw(sha1_b, 0, 8); + data.writeRaw(sha1_c, 16, 4); + data.writeRaw(sha1_d, 0, 8); + keyData.aesIv = data.toByteArray(); + data.cleanup(); + + return keyData; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/NetworkMessage.java b/TMessagesProj/src/main/java/org/telegram/messenger/NetworkMessage.java index a44ef7664..67cd93cf4 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NetworkMessage.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NetworkMessage.java @@ -1,9 +1,9 @@ /* - * This is the source code of Telegram for Android v. 1.3.2. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/RPCRequest.java b/TMessagesProj/src/main/java/org/telegram/messenger/RPCRequest.java index 180c9446b..63dd660f4 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/RPCRequest.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/RPCRequest.java @@ -1,9 +1,9 @@ /* - * This is the source code of Telegram for Android v. 1.3.2. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/SerializedData.java b/TMessagesProj/src/main/java/org/telegram/messenger/SerializedData.java index 40847dabb..d23048322 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SerializedData.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SerializedData.java @@ -1,9 +1,9 @@ /* - * This is the source code of Telegram for Android v. 1.3.2. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; @@ -47,6 +47,7 @@ public class SerializedData extends AbsSerializedData { isOut = false; inbuf = new ByteArrayInputStream(data); in = new DataInputStream(inbuf); + len = 0; } public void cleanup() { @@ -131,17 +132,6 @@ public class SerializedData extends AbsSerializedData { } } - public boolean readBool() { - int consructor = readInt32(); - if (consructor == 0x997275b5) { - return true; - } else if (consructor == 0xbc799737) { - return false; - } - FileLog.e("tmessages", "Not bool value!"); - return false; - } - public void writeBool(boolean value) { if (!justCalc) { if (value) { @@ -173,52 +163,6 @@ public class SerializedData extends AbsSerializedData { } } - public int readInt32() { - return readInt32(null); - } - - public int readInt32(boolean[] error) { - try { - int i = 0; - for(int j = 0; j < 4; j++) { - i |= (in.read() << (j * 8)); - } - if (error != null) { - error[0] = false; - } - return i; - } catch(Exception x) { - if (error != null) { - error[0] = true; - } - FileLog.e("tmessages", "read int32 error"); - } - return 0; - } - - public long readInt64() { - return readInt64(null); - } - - public long readInt64(boolean[] error) { - try { - long i = 0; - for(int j = 0; j < 8; j++) { - i |= ((long)in.read() << (j * 8)); - } - if (error != null) { - error[0] = false; - } - return i; - } catch (Exception x) { - if (error != null) { - error[0] = true; - } - FileLog.e("tmessages", "read int64 error"); - } - return 0; - } - public void writeRaw(byte[] b) { try { if (!justCalc) { @@ -226,7 +170,7 @@ public class SerializedData extends AbsSerializedData { } else { len += b.length; } - } catch (Exception x) { + } catch (Exception e) { FileLog.e("tmessages", "write raw error"); } } @@ -238,7 +182,7 @@ public class SerializedData extends AbsSerializedData { } else { len += count; } - } catch (Exception x) { + } catch (Exception e) { FileLog.e("tmessages", "write raw error"); } } @@ -246,7 +190,7 @@ public class SerializedData extends AbsSerializedData { public void writeByte(int i) { try { if (!justCalc) { - out.writeByte((byte)i); + out.writeByte((byte) i); } else { len += 1; } @@ -267,68 +211,6 @@ public class SerializedData extends AbsSerializedData { } } - public void readRaw(byte[] b) { - try { - in.read(b); - } catch (Exception x) { - FileLog.e("tmessages", "read raw error"); - } - } - - public byte[] readData(int count) { - byte[] arr = new byte[count]; - readRaw(arr); - return arr; - } - - public String readString() { - try { - int sl = 1; - int l = in.read(); - if(l >= 254) { - l = in.read() | (in.read() << 8) | (in.read() << 16); - sl = 4; - } - byte[] b = new byte[l]; - in.read(b); - int i=sl; - while((l + i) % 4 != 0) { - in.read(); - i++; - } - return new String(b, "UTF-8"); - } catch (Exception x) { - FileLog.e("tmessages", "read string error"); - } - return null; - } - - public byte[] readByteArray() { - try { - int sl = 1; - int l = in.read(); - if (l >= 254) { - l = in.read() | (in.read() << 8) | (in.read() << 16); - sl = 4; - } - byte[] b = new byte[l]; - in.read(b); - int i = sl; - while((l + i) % 4 != 0) { - in.read(); - i++; - } - return b; - } catch (Exception x) { - FileLog.e("tmessages", "read byte array error"); - } - return null; - } - - public ByteBufferDesc readByteBuffer() { - throw new RuntimeException("SerializedData don't support readByteBuffer"); - } - public void writeByteArray(byte[] b) { try { if (b.length <= 253) { @@ -361,7 +243,7 @@ public class SerializedData extends AbsSerializedData { } i++; } - } catch (Exception x) { + } catch (Exception e) { FileLog.e("tmessages", "write byte array error"); } } @@ -369,7 +251,7 @@ public class SerializedData extends AbsSerializedData { public void writeString(String s) { try { writeByteArray(s.getBytes("UTF-8")); - } catch(Exception x) { + } catch(Exception e) { FileLog.e("tmessages", "write string error"); } } @@ -406,24 +288,15 @@ public class SerializedData extends AbsSerializedData { } i++; } - } catch (Exception x) { + } catch (Exception e) { FileLog.e("tmessages", "write byte array error"); } } - public double readDouble() { - try { - return Double.longBitsToDouble(readInt64()); - } catch(Exception x) { - FileLog.e("tmessages", "read double error"); - } - return 0; - } - public void writeDouble(double d) { try { writeInt64(Double.doubleToRawLongBits(d)); - } catch(Exception x) { + } catch(Exception e) { FileLog.e("tmessages", "write double error"); } } @@ -444,4 +317,172 @@ public class SerializedData extends AbsSerializedData { public byte[] toByteArray() { return outbuf.toByteArray(); } + + public void skip(int count) { + if (count == 0) { + return; + } + if (!justCalc) { + if (in != null) { + try { + in.skipBytes(count); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + } else { + len += count; + } + } + + public int getPosition() { + return len; + } + + public boolean readBool(boolean exception) { + int consructor = readInt32(exception); + if (consructor == 0x997275b5) { + return true; + } else if (consructor == 0xbc799737) { + return false; + } + if (exception) { + throw new RuntimeException("Not bool value!"); + } else { + FileLog.e("tmessages", "Not bool value!"); + } + return false; + } + + public void readRaw(byte[] b, boolean exception) { + try { + in.read(b); + len += b.length; + } catch (Exception e) { + if (exception) { + throw new RuntimeException("read raw error", e); + } else { + FileLog.e("tmessages", "read raw error"); + } + } + } + + public byte[] readData(int count, boolean exception) { + byte[] arr = new byte[count]; + readRaw(arr, exception); + return arr; + } + + public String readString(boolean exception) { + try { + int sl = 1; + int l = in.read(); + len++; + if(l >= 254) { + l = in.read() | (in.read() << 8) | (in.read() << 16); + len += 3; + sl = 4; + } + byte[] b = new byte[l]; + in.read(b); + len++; + int i=sl; + while((l + i) % 4 != 0) { + in.read(); + len++; + i++; + } + return new String(b, "UTF-8"); + } catch (Exception e) { + if (exception) { + throw new RuntimeException("read string error", e); + } else { + FileLog.e("tmessages", "read string error"); + } + } + return null; + } + + public byte[] readByteArray(boolean exception) { + try { + int sl = 1; + int l = in.read(); + len++; + if (l >= 254) { + l = in.read() | (in.read() << 8) | (in.read() << 16); + len += 3; + sl = 4; + } + byte[] b = new byte[l]; + in.read(b); + len++; + int i = sl; + while((l + i) % 4 != 0) { + in.read(); + len++; + i++; + } + return b; + } catch (Exception e) { + if (exception) { + throw new RuntimeException("read byte array error", e); + } else { + FileLog.e("tmessages", "read byte array error"); + } + } + return null; + } + + public ByteBufferDesc readByteBuffer(boolean exception) { + throw new RuntimeException("SerializedData don't support readByteBuffer"); + } + + public double readDouble(boolean exception) { + try { + return Double.longBitsToDouble(readInt64(exception)); + } catch(Exception e) { + if (exception) { + throw new RuntimeException("read double error", e); + } else { + FileLog.e("tmessages", "read double error"); + } + } + return 0; + } + + public int readInt32(boolean exception) { + try { + int i = 0; + for(int j = 0; j < 4; j++) { + i |= (in.read() << (j * 8)); + len++; + } + return i; + } catch(Exception e) { + if (exception) { + throw new RuntimeException("read int32 error", e); + } else { + FileLog.e("tmessages", "read int32 error"); + } + } + return 0; + } + + public long readInt64(boolean exception) { + try { + long i = 0; + for(int j = 0; j < 8; j++) { + i |= ((long)in.read() << (j * 8)); + len++; + } + return i; + } catch (Exception e) { + if (exception) { + throw new RuntimeException("read int64 error", e); + } else { + FileLog.e("tmessages", "read int64 error"); + } + } + return 0; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ServerSalt.java b/TMessagesProj/src/main/java/org/telegram/messenger/ServerSalt.java index 33ca53ea2..88aa01958 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ServerSalt.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ServerSalt.java @@ -1,9 +1,9 @@ /* - * This is the source code of Telegram for Android v. 1.3.2. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/TLClassStore.java b/TMessagesProj/src/main/java/org/telegram/messenger/TLClassStore.java index c3e862ad6..1c855859e 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/TLClassStore.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/TLClassStore.java @@ -1,9 +1,9 @@ /* - * This is the source code of Telegram for Android v. 1.3.2. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; @@ -13,418 +13,89 @@ import java.util.HashMap; public class TLClassStore { private HashMap classStore; - public TLClassStore () { + public TLClassStore() { classStore = new HashMap<>(); - classStore.put(TLRPC.TL_chatPhotoEmpty.constructor, TLRPC.TL_chatPhotoEmpty.class); - classStore.put(TLRPC.TL_chatPhoto.constructor, TLRPC.TL_chatPhoto.class); classStore.put(TLRPC.TL_futuresalts.constructor, TLRPC.TL_futuresalts.class); - classStore.put(TLRPC.TL_bad_msg_notification.constructor, TLRPC.TL_bad_msg_notification.class); - classStore.put(TLRPC.TL_bad_server_salt.constructor, TLRPC.TL_bad_server_salt.class); + classStore.put(TLRPC.TL_msg_new_detailed_info.constructor, TLRPC.TL_msg_new_detailed_info.class); + classStore.put(TLRPC.TL_msg_detailed_info.constructor, TLRPC.TL_msg_detailed_info.class); classStore.put(TLRPC.TL_error.constructor, TLRPC.TL_error.class); - classStore.put(TLRPC.TL_messages_sentEncryptedMessage.constructor, TLRPC.TL_messages_sentEncryptedMessage.class); - classStore.put(TLRPC.TL_messages_sentEncryptedFile.constructor, TLRPC.TL_messages_sentEncryptedFile.class); - classStore.put(TLRPC.TL_notifyAll.constructor, TLRPC.TL_notifyAll.class); - classStore.put(TLRPC.TL_notifyChats.constructor, TLRPC.TL_notifyChats.class); - classStore.put(TLRPC.TL_notifyUsers.constructor, TLRPC.TL_notifyUsers.class); - classStore.put(TLRPC.TL_notifyPeer.constructor, TLRPC.TL_notifyPeer.class); - classStore.put(TLRPC.TL_auth_checkedPhone.constructor, TLRPC.TL_auth_checkedPhone.class); - classStore.put(TLRPC.TL_msgs_ack.constructor, TLRPC.TL_msgs_ack.class); - classStore.put(TLRPC.TL_messages_chatFull.constructor, TLRPC.TL_messages_chatFull.class); - classStore.put(TLRPC.TL_documentAttributeAnimated.constructor, TLRPC.TL_documentAttributeAnimated.class); - classStore.put(TLRPC.TL_documentAttributeAudio.constructor, TLRPC.TL_documentAttributeAudio.class); - classStore.put(TLRPC.TL_documentAttributeFilename.constructor, TLRPC.TL_documentAttributeFilename.class); - classStore.put(TLRPC.TL_documentAttributeVideo.constructor, TLRPC.TL_documentAttributeVideo.class); - classStore.put(TLRPC.TL_documentAttributeSticker.constructor, TLRPC.TL_documentAttributeSticker.class); - classStore.put(TLRPC.TL_documentAttributeImageSize.constructor, TLRPC.TL_documentAttributeImageSize.class); - classStore.put(TLRPC.TL_rpc_result.constructor, TLRPC.TL_rpc_result.class); - classStore.put(TLRPC.TL_contactStatus.constructor, TLRPC.TL_contactStatus.class); classStore.put(TLRPC.TL_auth_authorization.constructor, TLRPC.TL_auth_authorization.class); - classStore.put(TLRPC.TL_messages_messages.constructor, TLRPC.TL_messages_messages.class); - classStore.put(TLRPC.TL_messages_messagesSlice.constructor, TLRPC.TL_messages_messagesSlice.class); - classStore.put(TLRPC.TL_rpc_answer_unknown.constructor, TLRPC.TL_rpc_answer_unknown.class); - classStore.put(TLRPC.TL_rpc_answer_dropped.constructor, TLRPC.TL_rpc_answer_dropped.class); - classStore.put(TLRPC.TL_rpc_answer_dropped_running.constructor, TLRPC.TL_rpc_answer_dropped_running.class); - classStore.put(TLRPC.TL_contacts_link.constructor, TLRPC.TL_contacts_link.class); - classStore.put(TLRPC.TL_peerUser.constructor, TLRPC.TL_peerUser.class); - classStore.put(TLRPC.TL_peerChat.constructor, TLRPC.TL_peerChat.class); - classStore.put(TLRPC.TL_encryptedFile.constructor, TLRPC.TL_encryptedFile.class); - classStore.put(TLRPC.TL_encryptedFileEmpty.constructor, TLRPC.TL_encryptedFileEmpty.class); - classStore.put(TLRPC.TL_destroy_session_ok.constructor, TLRPC.TL_destroy_session_ok.class); - classStore.put(TLRPC.TL_destroy_session_none.constructor, TLRPC.TL_destroy_session_none.class); - classStore.put(TLRPC.TL_updates_differenceEmpty.constructor, TLRPC.TL_updates_differenceEmpty.class); - classStore.put(TLRPC.TL_updates_differenceSlice.constructor, TLRPC.TL_updates_differenceSlice.class); - 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_messageEmpty.constructor, TLRPC.TL_messageEmpty.class); - classStore.put(TLRPC.TL_message.constructor, TLRPC.TL_message.class); - classStore.put(TLRPC.TL_messageService.constructor, TLRPC.TL_messageService.class); - classStore.put(TLRPC.TL_inputPhoneContact.constructor, TLRPC.TL_inputPhoneContact.class); - classStore.put(TLRPC.TL_sendMessageGeoLocationAction.constructor, TLRPC.TL_sendMessageGeoLocationAction.class); - classStore.put(TLRPC.TL_sendMessageChooseContactAction.constructor, TLRPC.TL_sendMessageChooseContactAction.class); - classStore.put(TLRPC.TL_sendMessageTypingAction.constructor, TLRPC.TL_sendMessageTypingAction.class); - classStore.put(TLRPC.TL_sendMessageUploadDocumentAction.constructor, TLRPC.TL_sendMessageUploadDocumentAction.class); - classStore.put(TLRPC.TL_sendMessageRecordVideoAction.constructor, TLRPC.TL_sendMessageRecordVideoAction.class); - classStore.put(TLRPC.TL_sendMessageUploadPhotoAction.constructor, TLRPC.TL_sendMessageUploadPhotoAction.class); - classStore.put(TLRPC.TL_sendMessageUploadVideoAction.constructor, TLRPC.TL_sendMessageUploadVideoAction.class); - classStore.put(TLRPC.TL_sendMessageUploadAudioAction.constructor, TLRPC.TL_sendMessageUploadAudioAction.class); - classStore.put(TLRPC.TL_sendMessageCancelAction.constructor, TLRPC.TL_sendMessageCancelAction.class); - classStore.put(TLRPC.TL_sendMessageRecordAudioAction.constructor, TLRPC.TL_sendMessageRecordAudioAction.class); - classStore.put(TLRPC.TL_invokeAfterMsg.constructor, TLRPC.TL_invokeAfterMsg.class); - classStore.put(TLRPC.TL_messageMediaVideo.constructor, TLRPC.TL_messageMediaVideo.class); - classStore.put(TLRPC.TL_messageMediaPhoto.constructor, TLRPC.TL_messageMediaPhoto.class); - classStore.put(TLRPC.TL_messageMediaDocument.constructor, TLRPC.TL_messageMediaDocument.class); - classStore.put(TLRPC.TL_messageMediaGeo.constructor, TLRPC.TL_messageMediaGeo.class); - classStore.put(TLRPC.TL_messageMediaEmpty.constructor, TLRPC.TL_messageMediaEmpty.class); - classStore.put(TLRPC.TL_messageMediaAudio.constructor, TLRPC.TL_messageMediaAudio.class); - classStore.put(TLRPC.TL_messageMediaContact.constructor, TLRPC.TL_messageMediaContact.class); - classStore.put(TLRPC.TL_messageMediaUnsupported.constructor, TLRPC.TL_messageMediaUnsupported.class); - classStore.put(TLRPC.TL_auth_sentAppCode.constructor, TLRPC.TL_auth_sentAppCode.class); - classStore.put(TLRPC.TL_auth_sentCode.constructor, TLRPC.TL_auth_sentCode.class); - classStore.put(TLRPC.TL_peerNotifySettingsEmpty.constructor, TLRPC.TL_peerNotifySettingsEmpty.class); - classStore.put(TLRPC.TL_peerNotifySettings.constructor, TLRPC.TL_peerNotifySettings.class); - classStore.put(TLRPC.TL_msg_resend_req.constructor, TLRPC.TL_msg_resend_req.class); - classStore.put(TLRPC.TL_http_wait.constructor, TLRPC.TL_http_wait.class); - classStore.put(TLRPC.TL_contacts_blocked.constructor, TLRPC.TL_contacts_blocked.class); - classStore.put(TLRPC.TL_contacts_blockedSlice.constructor, TLRPC.TL_contacts_blockedSlice.class); - classStore.put(TLRPC.TL_inputGeoPoint.constructor, TLRPC.TL_inputGeoPoint.class); - classStore.put(TLRPC.TL_inputGeoPointEmpty.constructor, TLRPC.TL_inputGeoPointEmpty.class); - classStore.put(TLRPC.TL_help_inviteText.constructor, TLRPC.TL_help_inviteText.class); - classStore.put(TLRPC.TL_messages_dhConfigNotModified.constructor, TLRPC.TL_messages_dhConfigNotModified.class); - classStore.put(TLRPC.TL_messages_dhConfig.constructor, TLRPC.TL_messages_dhConfig.class); - 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); - classStore.put(TLRPC.TL_msgs_all_info.constructor, TLRPC.TL_msgs_all_info.class); - classStore.put(TLRPC.TL_inputPeerNotifySettings.constructor, TLRPC.TL_inputPeerNotifySettings.class); - classStore.put(TLRPC.TL_null.constructor, TLRPC.TL_null.class); - classStore.put(TLRPC.TL_inputUserSelf.constructor, TLRPC.TL_inputUserSelf.class); - classStore.put(TLRPC.TL_inputUserForeign.constructor, TLRPC.TL_inputUserForeign.class); - classStore.put(TLRPC.TL_inputUserEmpty.constructor, TLRPC.TL_inputUserEmpty.class); - classStore.put(TLRPC.TL_inputUserContact.constructor, TLRPC.TL_inputUserContact.class); - classStore.put(TLRPC.TL_p_q_inner_data.constructor, TLRPC.TL_p_q_inner_data.class); - classStore.put(TLRPC.TL_msgs_state_req.constructor, TLRPC.TL_msgs_state_req.class); - classStore.put(TLRPC.TL_boolTrue.constructor, TLRPC.TL_boolTrue.class); - classStore.put(TLRPC.TL_boolFalse.constructor, TLRPC.TL_boolFalse.class); - classStore.put(TLRPC.TL_auth_exportedAuthorization.constructor, TLRPC.TL_auth_exportedAuthorization.class); - classStore.put(TLRPC.TL_inputNotifyChats.constructor, TLRPC.TL_inputNotifyChats.class); - classStore.put(TLRPC.TL_inputNotifyPeer.constructor, TLRPC.TL_inputNotifyPeer.class); - classStore.put(TLRPC.TL_inputNotifyUsers.constructor, TLRPC.TL_inputNotifyUsers.class); - classStore.put(TLRPC.TL_inputNotifyGeoChatPeer.constructor, TLRPC.TL_inputNotifyGeoChatPeer.class); - classStore.put(TLRPC.TL_inputNotifyAll.constructor, TLRPC.TL_inputNotifyAll.class); - classStore.put(TLRPC.TL_inputAudioFileLocation.constructor, TLRPC.TL_inputAudioFileLocation.class); - classStore.put(TLRPC.TL_inputEncryptedFileLocation.constructor, TLRPC.TL_inputEncryptedFileLocation.class); - classStore.put(TLRPC.TL_inputVideoFileLocation.constructor, TLRPC.TL_inputVideoFileLocation.class); - classStore.put(TLRPC.TL_inputDocumentFileLocation.constructor, TLRPC.TL_inputDocumentFileLocation.class); - classStore.put(TLRPC.TL_inputFileLocation.constructor, TLRPC.TL_inputFileLocation.class); - classStore.put(TLRPC.TL_photos_photo.constructor, TLRPC.TL_photos_photo.class); - classStore.put(TLRPC.TL_userContact.constructor, TLRPC.TL_userContact.class); - classStore.put(TLRPC.TL_userRequest.constructor, TLRPC.TL_userRequest.class); - classStore.put(TLRPC.TL_userForeign.constructor, TLRPC.TL_userForeign.class); - classStore.put(TLRPC.TL_userDeleted.constructor, TLRPC.TL_userDeleted.class); - classStore.put(TLRPC.TL_userSelf.constructor, TLRPC.TL_userSelf.class); - classStore.put(TLRPC.TL_userEmpty.constructor, TLRPC.TL_userEmpty.class); - classStore.put(TLRPC.TL_geoChatMessage.constructor, TLRPC.TL_geoChatMessage.class); - classStore.put(TLRPC.TL_geoChatMessageService.constructor, TLRPC.TL_geoChatMessageService.class); - classStore.put(TLRPC.TL_geoChatMessageEmpty.constructor, TLRPC.TL_geoChatMessageEmpty.class); - classStore.put(TLRPC.TL_pong.constructor, TLRPC.TL_pong.class); - classStore.put(TLRPC.TL_messageActionChatEditPhoto.constructor, TLRPC.TL_messageActionChatEditPhoto.class); - classStore.put(TLRPC.TL_messageActionChatDeleteUser.constructor, TLRPC.TL_messageActionChatDeleteUser.class); - classStore.put(TLRPC.TL_messageActionChatDeletePhoto.constructor, TLRPC.TL_messageActionChatDeletePhoto.class); - classStore.put(TLRPC.TL_messageActionChatAddUser.constructor, TLRPC.TL_messageActionChatAddUser.class); - classStore.put(TLRPC.TL_messageActionChatCreate.constructor, TLRPC.TL_messageActionChatCreate.class); - classStore.put(TLRPC.TL_messageActionEmpty.constructor, TLRPC.TL_messageActionEmpty.class); - classStore.put(TLRPC.TL_messageActionChatEditTitle.constructor, TLRPC.TL_messageActionChatEditTitle.class); - classStore.put(TLRPC.TL_messageActionGeoChatCreate.constructor, TLRPC.TL_messageActionGeoChatCreate.class); - classStore.put(TLRPC.TL_messageActionGeoChatCheckin.constructor, TLRPC.TL_messageActionGeoChatCheckin.class); classStore.put(TLRPC.TL_dh_gen_retry.constructor, TLRPC.TL_dh_gen_retry.class); classStore.put(TLRPC.TL_dh_gen_fail.constructor, TLRPC.TL_dh_gen_fail.class); classStore.put(TLRPC.TL_dh_gen_ok.constructor, TLRPC.TL_dh_gen_ok.class); - classStore.put(TLRPC.TL_peerNotifyEventsEmpty.constructor, TLRPC.TL_peerNotifyEventsEmpty.class); - classStore.put(TLRPC.TL_peerNotifyEventsAll.constructor, TLRPC.TL_peerNotifyEventsAll.class); - classStore.put(TLRPC.TL_chatLocated.constructor, TLRPC.TL_chatLocated.class); - classStore.put(TLRPC.TL_decryptedMessageService.constructor, TLRPC.TL_decryptedMessageService.class); - classStore.put(TLRPC.TL_decryptedMessage.constructor, TLRPC.TL_decryptedMessage.class); - classStore.put(TLRPC.TL_inputPeerNotifyEventsAll.constructor, TLRPC.TL_inputPeerNotifyEventsAll.class); - classStore.put(TLRPC.TL_inputPeerNotifyEventsEmpty.constructor, TLRPC.TL_inputPeerNotifyEventsEmpty.class); - classStore.put(TLRPC.TL_client_DH_inner_data.constructor, TLRPC.TL_client_DH_inner_data.class); - classStore.put(TLRPC.TL_video.constructor, TLRPC.TL_video.class); - classStore.put(TLRPC.TL_videoEmpty.constructor, TLRPC.TL_videoEmpty.class); - classStore.put(TLRPC.TL_contactBlocked.constructor, TLRPC.TL_contactBlocked.class); - classStore.put(TLRPC.TL_inputDocumentEmpty.constructor, TLRPC.TL_inputDocumentEmpty.class); - classStore.put(TLRPC.TL_inputDocument.constructor, TLRPC.TL_inputDocument.class); - classStore.put(TLRPC.TL_inputAppEvent.constructor, TLRPC.TL_inputAppEvent.class); - 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); - classStore.put(TLRPC.TL_inputMediaDocument.constructor, TLRPC.TL_inputMediaDocument.class); - classStore.put(TLRPC.TL_inputMediaVideo.constructor, TLRPC.TL_inputMediaVideo.class); - classStore.put(TLRPC.TL_inputMediaGeoPoint.constructor, TLRPC.TL_inputMediaGeoPoint.class); - classStore.put(TLRPC.TL_inputMediaEmpty.constructor, TLRPC.TL_inputMediaEmpty.class); - classStore.put(TLRPC.TL_inputMediaUploadedThumbVideo.constructor, TLRPC.TL_inputMediaUploadedThumbVideo.class); - classStore.put(TLRPC.TL_inputMediaUploadedPhoto.constructor, TLRPC.TL_inputMediaUploadedPhoto.class); - classStore.put(TLRPC.TL_inputMediaUploadedAudio.constructor, TLRPC.TL_inputMediaUploadedAudio.class); - classStore.put(TLRPC.TL_inputMediaUploadedVideo.constructor, TLRPC.TL_inputMediaUploadedVideo.class); - classStore.put(TLRPC.TL_inputMediaUploadedDocument.constructor, TLRPC.TL_inputMediaUploadedDocument.class); - classStore.put(TLRPC.TL_inputMediaPhoto.constructor, TLRPC.TL_inputMediaPhoto.class); - classStore.put(TLRPC.TL_geochats_messagesSlice.constructor, TLRPC.TL_geochats_messagesSlice.class); - classStore.put(TLRPC.TL_geochats_messages.constructor, TLRPC.TL_geochats_messages.class); - classStore.put(TLRPC.TL_messages_sentMessage.constructor, TLRPC.TL_messages_sentMessage.class); - classStore.put(TLRPC.TL_messages_sentMessageLink.constructor, TLRPC.TL_messages_sentMessageLink.class); - classStore.put(TLRPC.TL_encryptedMessageService.constructor, TLRPC.TL_encryptedMessageService.class); - classStore.put(TLRPC.TL_encryptedMessage.constructor, TLRPC.TL_encryptedMessage.class); - 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_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_disabledFeature.constructor, TLRPC.TL_disabledFeature.class); + classStore.put(TLRPC.TL_server_DH_inner_data.constructor, TLRPC.TL_server_DH_inner_data.class); + classStore.put(TLRPC.TL_msgs_ack.constructor, TLRPC.TL_msgs_ack.class); classStore.put(TLRPC.TL_futureSalt.constructor, TLRPC.TL_futureSalt.class); - classStore.put(TLRPC.TL_updateEncryptedMessagesRead.constructor, TLRPC.TL_updateEncryptedMessagesRead.class); - classStore.put(TLRPC.TL_updateContactLink.constructor, TLRPC.TL_updateContactLink.class); - classStore.put(TLRPC.TL_updateReadMessages.constructor, TLRPC.TL_updateReadMessages.class); - classStore.put(TLRPC.TL_updateChatParticipantDelete.constructor, TLRPC.TL_updateChatParticipantDelete.class); - classStore.put(TLRPC.TL_updateServiceNotification.constructor, TLRPC.TL_updateServiceNotification.class); - classStore.put(TLRPC.TL_updateNotifySettings.constructor, TLRPC.TL_updateNotifySettings.class); - classStore.put(TLRPC.TL_updateUserTyping.constructor, TLRPC.TL_updateUserTyping.class); - classStore.put(TLRPC.TL_updateChatUserTyping.constructor, TLRPC.TL_updateChatUserTyping.class); - classStore.put(TLRPC.TL_updateUserName.constructor, TLRPC.TL_updateUserName.class); - classStore.put(TLRPC.TL_updateNewEncryptedMessage.constructor, TLRPC.TL_updateNewEncryptedMessage.class); - classStore.put(TLRPC.TL_updateNewMessage.constructor, TLRPC.TL_updateNewMessage.class); - classStore.put(TLRPC.TL_updateMessageID.constructor, TLRPC.TL_updateMessageID.class); - classStore.put(TLRPC.TL_updateDeleteMessages.constructor, TLRPC.TL_updateDeleteMessages.class); - 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); - classStore.put(TLRPC.TL_updateNewAuthorization.constructor, TLRPC.TL_updateNewAuthorization.class); - classStore.put(TLRPC.TL_updateNewGeoChatMessage.constructor, TLRPC.TL_updateNewGeoChatMessage.class); - classStore.put(TLRPC.TL_updateUserPhoto.constructor, TLRPC.TL_updateUserPhoto.class); - classStore.put(TLRPC.TL_updateContactRegistered.constructor, TLRPC.TL_updateContactRegistered.class); - classStore.put(TLRPC.TL_updateChatParticipantAdd.constructor, TLRPC.TL_updateChatParticipantAdd.class); - classStore.put(TLRPC.TL_updateUserStatus.constructor, TLRPC.TL_updateUserStatus.class); - classStore.put(TLRPC.TL_contacts_suggested.constructor, TLRPC.TL_contacts_suggested.class); + classStore.put(TLRPC.TL_msg_resend_req.constructor, TLRPC.TL_msg_resend_req.class); classStore.put(TLRPC.TL_rpc_error.constructor, TLRPC.TL_rpc_error.class); classStore.put(TLRPC.TL_rpc_req_error.constructor, TLRPC.TL_rpc_req_error.class); - classStore.put(TLRPC.TL_inputEncryptedFile.constructor, TLRPC.TL_inputEncryptedFile.class); - classStore.put(TLRPC.TL_inputEncryptedFileBigUploaded.constructor, TLRPC.TL_inputEncryptedFileBigUploaded.class); - classStore.put(TLRPC.TL_inputEncryptedFileEmpty.constructor, TLRPC.TL_inputEncryptedFileEmpty.class); - classStore.put(TLRPC.TL_inputEncryptedFileUploaded.constructor, TLRPC.TL_inputEncryptedFileUploaded.class); - classStore.put(TLRPC.TL_decryptedMessageActionFlushHistory.constructor, TLRPC.TL_decryptedMessageActionFlushHistory.class); - classStore.put(TLRPC.TL_decryptedMessageActionResend.constructor, TLRPC.TL_decryptedMessageActionResend.class); - classStore.put(TLRPC.TL_decryptedMessageActionNotifyLayer.constructor, TLRPC.TL_decryptedMessageActionNotifyLayer.class); - classStore.put(TLRPC.TL_decryptedMessageActionSetMessageTTL.constructor, TLRPC.TL_decryptedMessageActionSetMessageTTL.class); - classStore.put(TLRPC.TL_decryptedMessageActionDeleteMessages.constructor, TLRPC.TL_decryptedMessageActionDeleteMessages.class); - classStore.put(TLRPC.TL_decryptedMessageActionTyping.constructor, TLRPC.TL_decryptedMessageActionTyping.class); - classStore.put(TLRPC.TL_decryptedMessageActionReadMessages.constructor, TLRPC.TL_decryptedMessageActionReadMessages.class); - classStore.put(TLRPC.TL_decryptedMessageActionScreenshotMessages.constructor, TLRPC.TL_decryptedMessageActionScreenshotMessages.class); - classStore.put(TLRPC.TL_server_DH_inner_data.constructor, TLRPC.TL_server_DH_inner_data.class); + classStore.put(TLRPC.TL_decryptedMessageService.constructor, TLRPC.TL_decryptedMessageService.class); + classStore.put(TLRPC.TL_decryptedMessage.constructor, TLRPC.TL_decryptedMessage.class); + classStore.put(TLRPC.TL_bad_msg_notification.constructor, TLRPC.TL_bad_msg_notification.class); + classStore.put(TLRPC.TL_bad_server_salt.constructor, TLRPC.TL_bad_server_salt.class); classStore.put(TLRPC.TL_new_session_created.constructor, TLRPC.TL_new_session_created.class); - classStore.put(TLRPC.TL_account_password.constructor, TLRPC.TL_account_password.class); - classStore.put(TLRPC.TL_account_noPassword.constructor, TLRPC.TL_account_noPassword.class); - classStore.put(TLRPC.TL_userProfilePhotoEmpty.constructor, TLRPC.TL_userProfilePhotoEmpty.class); - classStore.put(TLRPC.TL_userProfilePhoto.constructor, TLRPC.TL_userProfilePhoto.class); - classStore.put(TLRPC.TL_photo.constructor, TLRPC.TL_photo.class); - classStore.put(TLRPC.TL_photoEmpty.constructor, TLRPC.TL_photoEmpty.class); - classStore.put(TLRPC.TL_encryptedChatWaiting.constructor, TLRPC.TL_encryptedChatWaiting.class); - classStore.put(TLRPC.TL_encryptedChatEmpty.constructor, TLRPC.TL_encryptedChatEmpty.class); - classStore.put(TLRPC.TL_encryptedChatDiscarded.constructor, TLRPC.TL_encryptedChatDiscarded.class); - classStore.put(TLRPC.TL_encryptedChat.constructor, TLRPC.TL_encryptedChat.class); - classStore.put(TLRPC.TL_encryptedChatRequested.constructor, TLRPC.TL_encryptedChatRequested.class); - classStore.put(TLRPC.TL_geochats_statedMessage.constructor, TLRPC.TL_geochats_statedMessage.class); - classStore.put(TLRPC.TL_contact.constructor, TLRPC.TL_contact.class); - classStore.put(TLRPC.TL_config.constructor, TLRPC.TL_config.class); - classStore.put(TLRPC.TL_inputAudio.constructor, TLRPC.TL_inputAudio.class); - classStore.put(TLRPC.TL_inputAudioEmpty.constructor, TLRPC.TL_inputAudioEmpty.class); - classStore.put(TLRPC.TL_help_support.constructor, TLRPC.TL_help_support.class); - classStore.put(TLRPC.TL_messages_chats.constructor, TLRPC.TL_messages_chats.class); - classStore.put(TLRPC.TL_contacts_found.constructor, TLRPC.TL_contacts_found.class); - classStore.put(TLRPC.TL_chatParticipants.constructor, TLRPC.TL_chatParticipants.class); - classStore.put(TLRPC.TL_chatParticipantsForbidden.constructor, TLRPC.TL_chatParticipantsForbidden.class); - classStore.put(TLRPC.TL_decryptedMessageMediaDocument.constructor, TLRPC.TL_decryptedMessageMediaDocument.class); - classStore.put(TLRPC.TL_decryptedMessageMediaGeoPoint.constructor, TLRPC.TL_decryptedMessageMediaGeoPoint.class); - classStore.put(TLRPC.TL_decryptedMessageMediaAudio.constructor, TLRPC.TL_decryptedMessageMediaAudio.class); - classStore.put(TLRPC.TL_decryptedMessageMediaVideo.constructor, TLRPC.TL_decryptedMessageMediaVideo.class); - classStore.put(TLRPC.TL_decryptedMessageMediaContact.constructor, TLRPC.TL_decryptedMessageMediaContact.class); - classStore.put(TLRPC.TL_decryptedMessageMediaEmpty.constructor, TLRPC.TL_decryptedMessageMediaEmpty.class); - classStore.put(TLRPC.TL_decryptedMessageMediaPhoto.constructor, TLRPC.TL_decryptedMessageMediaPhoto.class); - classStore.put(TLRPC.TL_chatParticipant.constructor, TLRPC.TL_chatParticipant.class); - classStore.put(TLRPC.TL_chatForbidden.constructor, TLRPC.TL_chatForbidden.class); - classStore.put(TLRPC.TL_geoChat.constructor, TLRPC.TL_geoChat.class); - classStore.put(TLRPC.TL_chatEmpty.constructor, TLRPC.TL_chatEmpty.class); - classStore.put(TLRPC.TL_chat.constructor, TLRPC.TL_chat.class); - classStore.put(TLRPC.TL_storage_fileUnknown.constructor, TLRPC.TL_storage_fileUnknown.class); - classStore.put(TLRPC.TL_storage_fileMp4.constructor, TLRPC.TL_storage_fileMp4.class); - classStore.put(TLRPC.TL_storage_fileWebp.constructor, TLRPC.TL_storage_fileWebp.class); - classStore.put(TLRPC.TL_storage_filePng.constructor, TLRPC.TL_storage_filePng.class); - classStore.put(TLRPC.TL_storage_fileGif.constructor, TLRPC.TL_storage_fileGif.class); - classStore.put(TLRPC.TL_storage_filePdf.constructor, TLRPC.TL_storage_filePdf.class); - classStore.put(TLRPC.TL_storage_fileMp3.constructor, TLRPC.TL_storage_fileMp3.class); - classStore.put(TLRPC.TL_storage_fileJpeg.constructor, TLRPC.TL_storage_fileJpeg.class); - classStore.put(TLRPC.TL_storage_fileMov.constructor, TLRPC.TL_storage_fileMov.class); - classStore.put(TLRPC.TL_storage_filePartial.constructor, TLRPC.TL_storage_filePartial.class); - classStore.put(TLRPC.TL_inputMessagesFilterVideo.constructor, TLRPC.TL_inputMessagesFilterVideo.class); - classStore.put(TLRPC.TL_inputMessagesFilterEmpty.constructor, TLRPC.TL_inputMessagesFilterEmpty.class); - classStore.put(TLRPC.TL_inputMessagesFilterPhotos.constructor, TLRPC.TL_inputMessagesFilterPhotos.class); - classStore.put(TLRPC.TL_inputMessagesFilterPhotoVideo.constructor, TLRPC.TL_inputMessagesFilterPhotoVideo.class); - classStore.put(TLRPC.TL_inputMessagesFilterDocument.constructor, TLRPC.TL_inputMessagesFilterDocument.class); - classStore.put(TLRPC.TL_inputMessagesFilterAudio.constructor, TLRPC.TL_inputMessagesFilterAudio.class); - classStore.put(TLRPC.TL_msgs_state_info.constructor, TLRPC.TL_msgs_state_info.class); - classStore.put(TLRPC.TL_upload_file.constructor, TLRPC.TL_upload_file.class); - classStore.put(TLRPC.TL_dialog.constructor, TLRPC.TL_dialog.class); - classStore.put(TLRPC.TL_fileLocation.constructor, TLRPC.TL_fileLocation.class); - classStore.put(TLRPC.TL_fileLocationUnavailable.constructor, TLRPC.TL_fileLocationUnavailable.class); - classStore.put(TLRPC.TL_messages_messageEmpty.constructor, TLRPC.TL_messages_messageEmpty.class); - classStore.put(TLRPC.TL_messages_message.constructor, TLRPC.TL_messages_message.class); - classStore.put(TLRPC.TL_geochats_located.constructor, TLRPC.TL_geochats_located.class); - classStore.put(TLRPC.TL_inputGeoChat.constructor, TLRPC.TL_inputGeoChat.class); - classStore.put(TLRPC.TL_protoMessage.constructor, TLRPC.TL_protoMessage.class); - classStore.put(TLRPC.TL_photoSize.constructor, TLRPC.TL_photoSize.class); - classStore.put(TLRPC.TL_photoSizeEmpty.constructor, TLRPC.TL_photoSizeEmpty.class); - classStore.put(TLRPC.TL_photoCachedSize.constructor, TLRPC.TL_photoCachedSize.class); - classStore.put(TLRPC.TL_contactFound.constructor, TLRPC.TL_contactFound.class); - classStore.put(TLRPC.TL_inputFileBig.constructor, TLRPC.TL_inputFileBig.class); - classStore.put(TLRPC.TL_inputFile.constructor, TLRPC.TL_inputFile.class); - classStore.put(TLRPC.TL_userFull.constructor, TLRPC.TL_userFull.class); - classStore.put(TLRPC.TL_updates_state.constructor, TLRPC.TL_updates_state.class); classStore.put(TLRPC.TL_resPQ.constructor, TLRPC.TL_resPQ.class); + classStore.put(TLRPC.TL_config.constructor, TLRPC.TL_config.class); + classStore.put(TLRPC.TL_msg_copy.constructor, TLRPC.TL_msg_copy.class); + classStore.put(TLRPC.TL_pong.constructor, TLRPC.TL_pong.class); + classStore.put(TLRPC.TL_rpc_answer_unknown.constructor, TLRPC.TL_rpc_answer_unknown.class); + classStore.put(TLRPC.TL_rpc_answer_dropped.constructor, TLRPC.TL_rpc_answer_dropped.class); + classStore.put(TLRPC.TL_rpc_answer_dropped_running.constructor, TLRPC.TL_rpc_answer_dropped_running.class); + classStore.put(TLRPC.TL_rpc_result.constructor, TLRPC.TL_rpc_result.class); + classStore.put(TLRPC.TL_auth_exportedAuthorization.constructor, TLRPC.TL_auth_exportedAuthorization.class); + classStore.put(TLRPC.TL_destroy_session_ok.constructor, TLRPC.TL_destroy_session_ok.class); + classStore.put(TLRPC.TL_destroy_session_none.constructor, TLRPC.TL_destroy_session_none.class); + classStore.put(TLRPC.TL_msgs_state_req.constructor, TLRPC.TL_msgs_state_req.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_protoMessage.constructor, TLRPC.TL_protoMessage.class); + classStore.put(TLRPC.TL_msgs_all_info.constructor, TLRPC.TL_msgs_all_info.class); + classStore.put(TLRPC.TL_p_q_inner_data.constructor, TLRPC.TL_p_q_inner_data.class); classStore.put(TLRPC.TL_updateShortChatMessage.constructor, TLRPC.TL_updateShortChatMessage.class); classStore.put(TLRPC.TL_updates.constructor, TLRPC.TL_updates.class); classStore.put(TLRPC.TL_updateShortMessage.constructor, TLRPC.TL_updateShortMessage.class); classStore.put(TLRPC.TL_updateShort.constructor, TLRPC.TL_updateShort.class); classStore.put(TLRPC.TL_updatesCombined.constructor, TLRPC.TL_updatesCombined.class); classStore.put(TLRPC.TL_updatesTooLong.constructor, TLRPC.TL_updatesTooLong.class); - classStore.put(TLRPC.TL_wallPaper.constructor, TLRPC.TL_wallPaper.class); - classStore.put(TLRPC.TL_wallPaperSolid.constructor, TLRPC.TL_wallPaperSolid.class); - classStore.put(TLRPC.TL_msg_new_detailed_info.constructor, TLRPC.TL_msg_new_detailed_info.class); - classStore.put(TLRPC.TL_msg_detailed_info.constructor, TLRPC.TL_msg_detailed_info.class); - classStore.put(TLRPC.TL_inputEncryptedChat.constructor, TLRPC.TL_inputEncryptedChat.class); - classStore.put(TLRPC.TL_inputChatPhoto.constructor, TLRPC.TL_inputChatPhoto.class); - classStore.put(TLRPC.TL_inputChatPhotoEmpty.constructor, TLRPC.TL_inputChatPhotoEmpty.class); - classStore.put(TLRPC.TL_inputChatUploadedPhoto.constructor, TLRPC.TL_inputChatUploadedPhoto.class); - classStore.put(TLRPC.TL_inputVideoEmpty.constructor, TLRPC.TL_inputVideoEmpty.class); - classStore.put(TLRPC.TL_inputVideo.constructor, TLRPC.TL_inputVideo.class); - classStore.put(TLRPC.TL_nearestDc.constructor, TLRPC.TL_nearestDc.class); - 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_stickerPack.constructor, TLRPC.TL_stickerPack.class); - classStore.put(TLRPC.TL_messages_allStickers.constructor, TLRPC.TL_messages_allStickers.class); - classStore.put(TLRPC.TL_messages_allStickersNotModified.constructor, TLRPC.TL_messages_allStickersNotModified.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); - classStore.put(TLRPC.TL_inputPeerSelf.constructor, TLRPC.TL_inputPeerSelf.class); - classStore.put(TLRPC.TL_inputPeerForeign.constructor, TLRPC.TL_inputPeerForeign.class); - classStore.put(TLRPC.TL_dcOption.constructor, TLRPC.TL_dcOption.class); + classStore.put(TLRPC.TL_msgs_state_info.constructor, TLRPC.TL_msgs_state_info.class); classStore.put(TLRPC.TL_decryptedMessageLayer.constructor, TLRPC.TL_decryptedMessageLayer.class); - classStore.put(TLRPC.TL_inputPhotoCropAuto.constructor, TLRPC.TL_inputPhotoCropAuto.class); - 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_decryptedMessageMediaExternalDocument.constructor, TLRPC.TL_decryptedMessageMediaExternalDocument.class); - classStore.put(TLRPC.TL_updateReadHistoryInbox.constructor, TLRPC.TL_updateReadHistoryInbox.class); - classStore.put(TLRPC.TL_updateReadHistoryOutbox.constructor, TLRPC.TL_updateReadHistoryOutbox.class); - classStore.put(TLRPC.TL_contactLinkUnknown.constructor, TLRPC.TL_contactLinkUnknown.class); - classStore.put(TLRPC.TL_contactLinkNone.constructor, TLRPC.TL_contactLinkNone.class); - classStore.put(TLRPC.TL_contactLinkHasPhone.constructor, TLRPC.TL_contactLinkHasPhone.class); - classStore.put(TLRPC.TL_contactLinkContact.constructor, TLRPC.TL_contactLinkContact.class); - classStore.put(TLRPC.TL_messages_affectedMessages.constructor, TLRPC.TL_messages_affectedMessages.class); - classStore.put(TLRPC.TL_updateWebPage.constructor, TLRPC.TL_updateWebPage.class); - classStore.put(TLRPC.TL_webPagePending.constructor, TLRPC.TL_webPagePending.class); - classStore.put(TLRPC.TL_webPageEmpty.constructor, TLRPC.TL_webPageEmpty.class); - classStore.put(TLRPC.TL_webPage.constructor, TLRPC.TL_webPage.class); - classStore.put(TLRPC.TL_messageMediaWebPage.constructor, TLRPC.TL_messageMediaWebPage.class); - classStore.put(TLRPC.TL_authorization.constructor, TLRPC.TL_authorization.class); - classStore.put(TLRPC.TL_account_authorizations.constructor, TLRPC.TL_account_authorizations.class); - classStore.put(TLRPC.TL_account_passwordSettings.constructor, TLRPC.TL_account_passwordSettings.class); - classStore.put(TLRPC.TL_account_passwordInputSettings.constructor, TLRPC.TL_account_passwordInputSettings.class); - classStore.put(TLRPC.TL_auth_passwordRecovery.constructor, TLRPC.TL_auth_passwordRecovery.class); - classStore.put(TLRPC.TL_messages_getWebPagePreview.constructor, TLRPC.TL_messages_getWebPagePreview.class); - - classStore.put(TLRPC.TL_messageMediaUnsupported_old.constructor, TLRPC.TL_messageMediaUnsupported_old.class); - classStore.put(TLRPC.TL_userSelf_old2.constructor, TLRPC.TL_userSelf_old2.class); - classStore.put(TLRPC.TL_msg_container.constructor, TLRPC.TL_msg_container.class); - classStore.put(TLRPC.TL_fileEncryptedLocation.constructor, TLRPC.TL_fileEncryptedLocation.class); - classStore.put(TLRPC.TL_messageActionTTLChange.constructor, TLRPC.TL_messageActionTTLChange.class); - classStore.put(TLRPC.TL_videoEncrypted.constructor, TLRPC.TL_videoEncrypted.class); - classStore.put(TLRPC.TL_documentEncrypted.constructor, TLRPC.TL_documentEncrypted.class); - classStore.put(TLRPC.TL_audioEncrypted.constructor, TLRPC.TL_audioEncrypted.class); + classStore.put(TLRPC.TL_http_wait.constructor, TLRPC.TL_http_wait.class); classStore.put(TLRPC.TL_gzip_packed.constructor, TLRPC.TL_gzip_packed.class); - classStore.put(TLRPC.Vector.constructor, TLRPC.Vector.class); - classStore.put(TLRPC.TL_userProfilePhotoOld.constructor, TLRPC.TL_userProfilePhotoOld.class); - classStore.put(TLRPC.TL_messageActionUserUpdatedPhoto.constructor, TLRPC.TL_messageActionUserUpdatedPhoto.class); - classStore.put(TLRPC.TL_messageActionUserJoined.constructor, TLRPC.TL_messageActionUserJoined.class); - classStore.put(TLRPC.TL_messageActionLoginUnknownLocation.constructor, TLRPC.TL_messageActionLoginUnknownLocation.class); - classStore.put(TLRPC.TL_encryptedChat_old.constructor, TLRPC.TL_encryptedChat_old.class); - classStore.put(TLRPC.TL_encryptedChatRequested_old.constructor, TLRPC.TL_encryptedChatRequested_old.class); - classStore.put(TLRPC.TL_decryptedMessageMediaVideo_old.constructor, TLRPC.TL_decryptedMessageMediaVideo_old.class); - classStore.put(TLRPC.TL_decryptedMessageMediaAudio_old.constructor, TLRPC.TL_decryptedMessageMediaAudio_old.class); - classStore.put(TLRPC.TL_audio_old.constructor, TLRPC.TL_audio_old.class); - classStore.put(TLRPC.TL_video_old.constructor, TLRPC.TL_video_old.class); - classStore.put(TLRPC.TL_messageActionCreatedBroadcastList.constructor, TLRPC.TL_messageActionCreatedBroadcastList.class); - classStore.put(TLRPC.TL_messageForwarded_old.constructor, TLRPC.TL_messageForwarded_old.class); - classStore.put(TLRPC.TL_message_old.constructor, TLRPC.TL_message_old.class); - classStore.put(TLRPC.TL_messageService_old.constructor, TLRPC.TL_messageService_old.class); classStore.put(TLRPC.TL_decryptedMessageService_old.constructor, TLRPC.TL_decryptedMessageService_old.class); classStore.put(TLRPC.TL_decryptedMessage_old.constructor, TLRPC.TL_decryptedMessage_old.class); classStore.put(TLRPC.TL_message_secret.constructor, TLRPC.TL_message_secret.class); - classStore.put(TLRPC.TL_userSelf_old.constructor, TLRPC.TL_userSelf_old.class); - classStore.put(TLRPC.TL_userContact_old.constructor, TLRPC.TL_userContact_old.class); - classStore.put(TLRPC.TL_userRequest_old.constructor, TLRPC.TL_userRequest_old.class); - 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); + classStore.put(TLRPC.TL_client_DH_inner_data.constructor, TLRPC.TL_client_DH_inner_data.class); + classStore.put(TLRPC.TL_null.constructor, TLRPC.TL_null.class); + classStore.put(TLRPC.TL_destroy_sessions_res.constructor, TLRPC.TL_destroy_sessions_res.class); + classStore.put(TLRPC.TL_msg_container.constructor, TLRPC.TL_msg_container.class); + + + classStore.put(TLRPC.TL_video.constructor, TLRPC.TL_video.class); + classStore.put(TLRPC.TL_videoEmpty.constructor, TLRPC.TL_videoEmpty.class); + classStore.put(TLRPC.TL_video_old2.constructor, TLRPC.TL_video_old2.class); + classStore.put(TLRPC.TL_video_old.constructor, TLRPC.TL_video_old.class); + classStore.put(TLRPC.TL_videoEncrypted.constructor, TLRPC.TL_videoEncrypted.class); + + classStore.put(TLRPC.TL_audio.constructor, TLRPC.TL_audio.class); + classStore.put(TLRPC.TL_audioEncrypted.constructor, TLRPC.TL_audioEncrypted.class); + classStore.put(TLRPC.TL_audioEmpty.constructor, TLRPC.TL_audioEmpty.class); + classStore.put(TLRPC.TL_audio_old.constructor, TLRPC.TL_audio_old.class); + + classStore.put(TLRPC.TL_document.constructor, TLRPC.TL_document.class); + classStore.put(TLRPC.TL_documentEmpty.constructor, TLRPC.TL_documentEmpty.class); classStore.put(TLRPC.TL_documentEncrypted_old.constructor, TLRPC.TL_documentEncrypted_old.class); + classStore.put(TLRPC.TL_documentEncrypted.constructor, TLRPC.TL_documentEncrypted.class); classStore.put(TLRPC.TL_document_old.constructor, TLRPC.TL_document_old.class); - classStore.put(TLRPC.TL_config_old.constructor, TLRPC.TL_config_old.class); - classStore.put(TLRPC.TL_messageForwarded_old2.constructor, TLRPC.TL_messageForwarded_old2.class); - classStore.put(TLRPC.TL_message_old2.constructor, TLRPC.TL_message_old2.class); - classStore.put(TLRPC.TL_documentAttributeSticker_old.constructor, TLRPC.TL_documentAttributeSticker_old.class); + + classStore.put(TLRPC.TL_photo.constructor, TLRPC.TL_photo.class); + classStore.put(TLRPC.TL_photoEmpty.constructor, TLRPC.TL_photoEmpty.class); + classStore.put(TLRPC.TL_photoSize.constructor, TLRPC.TL_photoSize.class); + classStore.put(TLRPC.TL_photoSizeEmpty.constructor, TLRPC.TL_photoSizeEmpty.class); + classStore.put(TLRPC.TL_photoCachedSize.constructor, TLRPC.TL_photoCachedSize.class); + classStore.put(TLRPC.TL_photo_old.constructor, TLRPC.TL_photo_old.class); } static TLClassStore store = null; @@ -436,46 +107,19 @@ public class TLClassStore { return store; } - public TLObject TLdeserialize(AbsSerializedData stream, int constructor) { - try { - return TLdeserialize(stream, constructor, null); - } catch (Exception e) { - return null; - } - } - - public TLObject TLdeserialize(AbsSerializedData stream, int constructor, TLObject request) { + public TLObject TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { Class objClass = classStore.get(constructor); if (objClass != null) { + TLObject response; try { - TLObject response = (TLObject)objClass.newInstance(); - if (response instanceof TLRPC.Vector) { - if (request != null) { - request.parseVector((TLRPC.Vector)response, stream); - } else { - int size = stream.readInt32(); - for (int a = 0; a < size; a++) { - ((TLRPC.Vector)response).objects.add(stream.readInt32()); - } - } - } else { - response.readParams(stream); - } - return response; - } catch (IllegalAccessException e) { - FileLog.e("tmessages", "can't create class"); - return null; - } catch (InstantiationException e2) { - FileLog.e("tmessages", "can't create class"); - return null; - } - } else { - FileLog.e("tmessages", String.format("unknown class %x", constructor)); - if (BuildVars.DEBUG_VERSION) { - throw new RuntimeException(String.format("unknown class %x", constructor)); - } else { + response = (TLObject) objClass.newInstance(); + } catch (Throwable e) { + FileLog.e("tmessages", e); return null; } + response.readParams(stream, exception); + return response; } + return null; } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/TLObject.java b/TMessagesProj/src/main/java/org/telegram/messenger/TLObject.java index 8ee8bc002..12cb748fe 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/TLObject.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/TLObject.java @@ -1,42 +1,35 @@ /* - * This is the source code of Telegram for Android v. 1.3.2. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; public class TLObject { + public boolean disableFree = false; - public TLObject () { + public TLObject() { } - public void readParams(AbsSerializedData stream) { + public void readParams(AbsSerializedData stream, boolean exception) { } - public byte[] serialize () { - return null; - } - public void serializeToStream(AbsSerializedData stream) { } - public Class responseClass () { - return this.getClass(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return null; } - public int layer () { - return 11; - } - - public void parseVector(TLRPC.Vector vector, AbsSerializedData data) { - + public int layer() { + return 11; } public void freeResources() { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/TLRPC.java b/TMessagesProj/src/main/java/org/telegram/messenger/TLRPC.java index 0789e6cbe..32d39df40 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/TLRPC.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/TLRPC.java @@ -1,15 +1,14 @@ /* - * This is the source code of Telegram for Android v. 1.3.2. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; import java.util.ArrayList; -import java.util.Locale; @SuppressWarnings("unchecked") public class TLRPC { @@ -19,2100 +18,37 @@ public class TLRPC { public static final int MESSAGE_FLAG_FWD = 4; public static final int MESSAGE_FLAG_REPLY = 8; public static final int MESSAGE_FLAG_MENTION = 16; - public static final int LAYER = 27; + public static final int MESSAGE_FLAG_CONTENT_UNREAD = 32; + public static final int LAYER = 30; - public static class ChatPhoto extends TLObject { - public FileLocation photo_small; - public FileLocation photo_big; - } + public static class TL_inputEncryptedChat extends TLObject { + public static int constructor = 0xf141b5e1; - public static class TL_chatPhotoEmpty extends ChatPhoto { - public static int constructor = 0x37c1011c; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_chatPhoto extends ChatPhoto { - public static int constructor = 0x6153276a; - - - public void readParams(AbsSerializedData stream) { - photo_small = (FileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - photo_big = (FileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - photo_small.serializeToStream(stream); - photo_big.serializeToStream(stream); - } - } - - public static class BadMsgNotification extends TLObject { - public long bad_msg_id; - public int bad_msg_seqno; - public int error_code; - public long new_server_salt; - } - - public static class TL_bad_msg_notification extends BadMsgNotification { - public static int constructor = 0xa7eff811; - - - public void readParams(AbsSerializedData stream) { - bad_msg_id = stream.readInt64(); - bad_msg_seqno = stream.readInt32(); - error_code = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(bad_msg_id); - stream.writeInt32(bad_msg_seqno); - stream.writeInt32(error_code); - } - } - - public static class TL_bad_server_salt extends BadMsgNotification { - public static int constructor = 0xedab447b; - - - public void readParams(AbsSerializedData stream) { - bad_msg_id = stream.readInt64(); - bad_msg_seqno = stream.readInt32(); - error_code = stream.readInt32(); - new_server_salt = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(bad_msg_id); - stream.writeInt32(bad_msg_seqno); - stream.writeInt32(error_code); - stream.writeInt64(new_server_salt); - } - } - - public static class TL_error extends TLObject { - public static int constructor = 0xc4b9f9bb; - - public int code; - public String text; - - public void readParams(AbsSerializedData stream) { - code = stream.readInt32(); - text = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(code); - stream.writeString(text); - } - } - - public static class messages_SentEncryptedMessage extends TLObject { - public int date; - public EncryptedFile file; - } - - public static class TL_messages_sentEncryptedMessage extends messages_SentEncryptedMessage { - public static int constructor = 0x560f8935; - - - public void readParams(AbsSerializedData stream) { - date = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(date); - } - } - - public static class TL_messages_sentEncryptedFile extends messages_SentEncryptedMessage { - public static int constructor = 0x9493ff32; - - - public void readParams(AbsSerializedData stream) { - date = stream.readInt32(); - file = (EncryptedFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(date); - file.serializeToStream(stream); - } - } - - public static class NotifyPeer extends TLObject { - public Peer peer; - } - - public static class TL_notifyAll extends NotifyPeer { - public static int constructor = 0x74d07c60; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_notifyChats extends NotifyPeer { - public static int constructor = 0xc007cec3; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_notifyUsers extends NotifyPeer { - public static int constructor = 0xb4c83b4c; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_notifyPeer extends NotifyPeer { - public static int constructor = 0x9fd40bd8; - - - public void readParams(AbsSerializedData stream) { - peer = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - peer.serializeToStream(stream); - } - } - - public static class TL_auth_checkedPhone extends TLObject { - public static int constructor = 0x811ea28e; - - public boolean phone_registered; - - public void readParams(AbsSerializedData stream) { - phone_registered = stream.readBool(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeBool(phone_registered); - } - } - - public static class TL_msgs_ack extends TLObject { - public static int constructor = 0x62d6b459; - - public ArrayList msg_ids = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - msg_ids.add(stream.readInt64()); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = msg_ids.size(); - stream.writeInt32(count); - for (Long msg_id : msg_ids) { - stream.writeInt64(msg_id); - } - } - } - - public static class TL_messages_chatFull extends TLObject { - public static int constructor = 0xe5d7d19c; - - public TL_chatFull full_chat; - public ArrayList chats = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - full_chat = (TL_chatFull)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - chats.add((Chat)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); - full_chat.serializeToStream(stream); - stream.writeInt32(0x1cb5c415); - int count = chats.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - chats.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 TL_account_passwordSettings extends TLObject { - public static int constructor = 0xb7b72ab3; - - public String email; - - public void readParams(AbsSerializedData stream) { - email = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(email); - } - } - - public static class DocumentAttribute extends TLObject { - public int duration; - public String file_name; - public String alt; - public int w; - public int h; - } - - public static class TL_documentAttributeAnimated extends DocumentAttribute { - public static int constructor = 0x11b58939; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_documentAttributeAudio extends DocumentAttribute { - public static int constructor = 0x51448e5; - - - public void readParams(AbsSerializedData stream) { - duration = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(duration); - } - } - - public static class TL_documentAttributeFilename extends DocumentAttribute { - public static int constructor = 0x15590068; - - - public void readParams(AbsSerializedData stream) { - file_name = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(file_name); - } - } - - public static class TL_documentAttributeVideo extends DocumentAttribute { - public static int constructor = 0x5910cccb; - - - public void readParams(AbsSerializedData stream) { - duration = stream.readInt32(); - w = stream.readInt32(); - h = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(duration); - stream.writeInt32(w); - stream.writeInt32(h); - } - } - - public static class TL_documentAttributeSticker extends DocumentAttribute { - public static int constructor = 0x994c9882; - - - public void readParams(AbsSerializedData stream) { - alt = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(alt); - } - } - - public static class TL_documentAttributeImageSize extends DocumentAttribute { - public static int constructor = 0x6c37c15c; - - - public void readParams(AbsSerializedData stream) { - w = stream.readInt32(); - h = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(w); - stream.writeInt32(h); - } - } - - public static class TL_contactStatus extends TLObject { - public static int constructor = 0xd3680c61; - - public int user_id; - public UserStatus status; - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - status.serializeToStream(stream); - } - } - - public static class TL_auth_authorization extends TLObject { - public static int constructor = 0xf6b673a4; - - public int expires; - public User user; - - public void readParams(AbsSerializedData stream) { - expires = stream.readInt32(); - user = (User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(expires); - user.serializeToStream(stream); - } - } - - public static class messages_Messages extends TLObject { - public ArrayList messages = new ArrayList<>(); - public ArrayList chats = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - public int count; - } - - public static class TL_messages_messages extends messages_Messages { - public static int constructor = 0x8c718e87; - - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - messages.add((Message)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - chats.add((Chat)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 = messages.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - messages.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = chats.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - chats.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 TL_messages_messagesSlice extends messages_Messages { - public static int constructor = 0xb446ae3; - - - public void readParams(AbsSerializedData stream) { - count = stream.readInt32(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - messages.add((Message)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - chats.add((Chat)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(count); - stream.writeInt32(0x1cb5c415); - int count = messages.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - messages.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = chats.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - chats.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 RpcDropAnswer extends TLObject { - public long msg_id; - public int seq_no; - public int bytes; - } - - public static class TL_rpc_answer_unknown extends RpcDropAnswer { - public static int constructor = 0x5e2ad36e; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_rpc_answer_dropped extends RpcDropAnswer { - public static int constructor = 0xa43ad8b7; - - - public void readParams(AbsSerializedData stream) { - msg_id = stream.readInt64(); - seq_no = stream.readInt32(); - bytes = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(msg_id); - stream.writeInt32(seq_no); - stream.writeInt32(bytes); - } - } - - public static class TL_rpc_answer_dropped_running extends RpcDropAnswer { - public static int constructor = 0xcd78e586; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_contacts_link extends TLObject { - public static int constructor = 0x3ace484c; - - public ContactLink my_link; - public ContactLink foreign_link; - public User user; - - public void readParams(AbsSerializedData stream) { - my_link = (ContactLink)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - foreign_link = (ContactLink)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - user = (User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - my_link.serializeToStream(stream); - foreign_link.serializeToStream(stream); - user.serializeToStream(stream); - } - } - - public static class Peer extends TLObject { - public int user_id; public int chat_id; - } + public long access_hash; - public static class TL_peerUser extends Peer { - public static int constructor = 0x9db1bc6d; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); + public static TL_inputEncryptedChat TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_inputEncryptedChat.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_inputEncryptedChat", constructor)); + } else { + return null; + } + } + TL_inputEncryptedChat result = new TL_inputEncryptedChat(); + result.readParams(stream, exception); + return result; } - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - } - } - - public static class TL_peerChat extends Peer { - public static int constructor = 0xbad0e5bb; - - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + access_hash = stream.readInt64(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(chat_id); - } - } - - public static class EncryptedFile extends TLObject { - public long id; - public long access_hash; - public int size; - public int dc_id; - public int key_fingerprint; - } - - public static class TL_encryptedFile extends EncryptedFile { - public static int constructor = 0x4a70994c; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - size = stream.readInt32(); - dc_id = stream.readInt32(); - key_fingerprint = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); stream.writeInt64(access_hash); - stream.writeInt32(size); - stream.writeInt32(dc_id); - stream.writeInt32(key_fingerprint); - } - } - - public static class TL_encryptedFileEmpty extends EncryptedFile { - public static int constructor = 0xc21f497e; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_messages_affectedMessages extends TLObject { - public static int constructor = 0x84d19185; - - public int pts; - public int pts_count; - - public void readParams(AbsSerializedData stream) { - pts = stream.readInt32(); - pts_count = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(pts); - stream.writeInt32(pts_count); - } - } - - public static class TL_authorization extends TLObject { - public static int constructor = 0x7bf2e6f6; - - public long hash; - public int flags; - public String device_model; - public String platform; - public String system_version; - public int api_id; - public String app_name; - public String app_version; - public int date_created; - public int date_active; - public String ip; - public String country; - public String region; - - public void readParams(AbsSerializedData stream) { - hash = stream.readInt64(); - flags = stream.readInt32(); - device_model = stream.readString(); - platform = stream.readString(); - system_version = stream.readString(); - api_id = stream.readInt32(); - app_name = stream.readString(); - app_version = stream.readString(); - date_created = stream.readInt32(); - date_active = stream.readInt32(); - ip = stream.readString(); - country = stream.readString(); - region = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(hash); - stream.writeInt32(flags); - stream.writeString(device_model); - stream.writeString(platform); - stream.writeString(system_version); - stream.writeInt32(api_id); - stream.writeString(app_name); - stream.writeString(app_version); - stream.writeInt32(date_created); - stream.writeInt32(date_active); - stream.writeString(ip); - stream.writeString(country); - stream.writeString(region); - } - } - - public static class DestroySessionRes extends TLObject { - public long session_id; - } - - public static class TL_destroy_session_ok extends DestroySessionRes { - public static int constructor = 0xe22045fc; - - - public void readParams(AbsSerializedData stream) { - session_id = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(session_id); - } - } - - public static class TL_destroy_session_none extends DestroySessionRes { - public static int constructor = 0x62d350c9; - - - public void readParams(AbsSerializedData stream) { - session_id = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(session_id); - } - } - - public static class updates_Difference extends TLObject { - public int date; - public int seq; - public ArrayList new_messages = new ArrayList<>(); - public ArrayList new_encrypted_messages = new ArrayList<>(); - public ArrayList other_updates = new ArrayList<>(); - public ArrayList chats = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - public TL_updates_state intermediate_state; - public TL_updates_state state; - } - - public static class TL_updates_differenceEmpty extends updates_Difference { - public static int constructor = 0x5d75a138; - - - public void readParams(AbsSerializedData stream) { - date = stream.readInt32(); - seq = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(date); - stream.writeInt32(seq); - } - } - - public static class TL_updates_differenceSlice extends updates_Difference { - public static int constructor = 0xa8fb1981; - - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - new_messages.add((Message)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - new_encrypted_messages.add((EncryptedMessage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - other_updates.add((Update)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - chats.add((Chat)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())); - } - intermediate_state = (TL_updates_state)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = new_messages.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - new_messages.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = new_encrypted_messages.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - new_encrypted_messages.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = other_updates.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - other_updates.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = chats.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - chats.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); - } - intermediate_state.serializeToStream(stream); - } - } - - public static class TL_updates_difference extends updates_Difference { - public static int constructor = 0xf49ca0; - - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - new_messages.add((Message)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - new_encrypted_messages.add((EncryptedMessage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - other_updates.add((Update)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - chats.add((Chat)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())); - } - state = (TL_updates_state)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = new_messages.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - new_messages.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = new_encrypted_messages.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - new_encrypted_messages.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = other_updates.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - other_updates.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = chats.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - chats.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); - } - state.serializeToStream(stream); - } - } - - public static class GeoPoint extends TLObject { - public double _long; - public double lat; - } - - public static class TL_geoPointEmpty extends GeoPoint { - public static int constructor = 0x1117dd5f; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_geoPoint extends GeoPoint { - public static int constructor = 0x2049d70c; - - - public void readParams(AbsSerializedData stream) { - _long = stream.readDouble(); - lat = stream.readDouble(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeDouble(_long); - stream.writeDouble(lat); - } - } - - 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 rules = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - - 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; - public String url; - public String text; - } - - public static class TL_help_appUpdate extends help_AppUpdate { - public static int constructor = 0x8987f311; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - critical = stream.readBool(); - url = stream.readString(); - text = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeBool(critical); - stream.writeString(url); - stream.writeString(text); - } - } - - public static class TL_help_noAppUpdate extends help_AppUpdate { - public static int constructor = 0xc45a6536; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_messageEmpty extends Message { - public static int constructor = 0x83e5de54; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - } - } - - public static class TL_inputPhoneContact extends TLObject { - public static int constructor = 0xf392b7f4; - - public long client_id; - public String phone; - public String first_name; - public String last_name; - - public void readParams(AbsSerializedData stream) { - client_id = stream.readInt64(); - phone = stream.readString(); - first_name = stream.readString(); - last_name = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(client_id); - stream.writeString(phone); - stream.writeString(first_name); - stream.writeString(last_name); - } - } - - public static class SendMessageAction extends TLObject { - } - - public static class TL_sendMessageGeoLocationAction extends SendMessageAction { - public static int constructor = 0x176f8ba1; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_sendMessageChooseContactAction extends SendMessageAction { - public static int constructor = 0x628cbc6f; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_sendMessageTypingAction extends SendMessageAction { - public static int constructor = 0x16bf744e; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_sendMessageUploadDocumentAction extends SendMessageAction { - public static int constructor = 0x8faee98e; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_sendMessageRecordVideoAction extends SendMessageAction { - public static int constructor = 0xa187d66f; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_sendMessageUploadPhotoAction extends SendMessageAction { - public static int constructor = 0x990a3c1a; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_sendMessageUploadVideoAction extends SendMessageAction { - public static int constructor = 0x92042ff7; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_sendMessageUploadAudioAction extends SendMessageAction { - public static int constructor = 0xe6ac8a6f; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_sendMessageCancelAction extends SendMessageAction { - public static int constructor = 0xfd5ec8f5; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_sendMessageRecordAudioAction extends SendMessageAction { - public static int constructor = 0xd52f73f7; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_invokeAfterMsg extends TLObject { - public static int constructor = 0xcb9f372d; - - public long msg_id; - public TLObject query; - - public void readParams(AbsSerializedData stream) { - msg_id = stream.readInt64(); - query = TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(msg_id); - query.serializeToStream(stream); - } - } - - public static class MessageMedia extends TLObject { - public Video video; - public Photo photo; - public WebPage webpage; - public Document document; - public GeoPoint geo; - public Audio audio; - public String phone_number; - public String first_name; - public String last_name; - public int user_id; - public byte[] bytes; - } - - public static class TL_messageMediaVideo extends MessageMedia { - public static int constructor = 0xa2d24290; - - - public void readParams(AbsSerializedData stream) { - video = (Video)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - video.serializeToStream(stream); - } - } - - public static class TL_messageMediaPhoto extends MessageMedia { - public static int constructor = 0xc8c45a2a; - - - public void readParams(AbsSerializedData stream) { - photo = (Photo)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - photo.serializeToStream(stream); - } - } - - public static class TL_messageMediaWebPage extends MessageMedia { - public static int constructor = 0xa32dd600; - - - public void readParams(AbsSerializedData stream) { - webpage = (WebPage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - webpage.serializeToStream(stream); - } - } - - public static class TL_messageMediaDocument extends MessageMedia { - public static int constructor = 0x2fda2204; - - - public void readParams(AbsSerializedData stream) { - document = (Document)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - document.serializeToStream(stream); - } - } - - public static class TL_messageMediaGeo extends MessageMedia { - public static int constructor = 0x56e0d474; - - - public void readParams(AbsSerializedData stream) { - geo = (GeoPoint)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - geo.serializeToStream(stream); - } - } - - public static class TL_messageMediaEmpty extends MessageMedia { - public static int constructor = 0x3ded6320; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_messageMediaAudio extends MessageMedia { - public static int constructor = 0xc6b68300; - - - public void readParams(AbsSerializedData stream) { - audio = (Audio)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - audio.serializeToStream(stream); - } - } - - public static class TL_messageMediaContact extends MessageMedia { - public static int constructor = 0x5e7d2f39; - - - public void readParams(AbsSerializedData stream) { - phone_number = stream.readString(); - first_name = stream.readString(); - last_name = stream.readString(); - user_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(phone_number); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeInt32(user_id); - } - } - - public static class TL_messageMediaUnsupported extends MessageMedia { - public static int constructor = 0x9f84f49e; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class auth_SentCode extends TLObject { - public boolean phone_registered; - public String phone_code_hash; - public int send_call_timeout; - public boolean is_password; - } - - public static class TL_auth_sentAppCode extends auth_SentCode { - public static int constructor = 0xe325edcf; - - - public void readParams(AbsSerializedData stream) { - phone_registered = stream.readBool(); - phone_code_hash = stream.readString(); - send_call_timeout = stream.readInt32(); - is_password = stream.readBool(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeBool(phone_registered); - stream.writeString(phone_code_hash); - stream.writeInt32(send_call_timeout); - stream.writeBool(is_password); - } - } - - public static class TL_auth_sentCode extends auth_SentCode { - public static int constructor = 0xefed51d9; - - - public void readParams(AbsSerializedData stream) { - phone_registered = stream.readBool(); - phone_code_hash = stream.readString(); - send_call_timeout = stream.readInt32(); - is_password = stream.readBool(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeBool(phone_registered); - stream.writeString(phone_code_hash); - stream.writeInt32(send_call_timeout); - stream.writeBool(is_password); - } - } - - public static class PeerNotifySettings extends TLObject { - public int mute_until; - public String sound; - public boolean show_previews; - public int events_mask; - } - - public static class TL_peerNotifySettingsEmpty extends PeerNotifySettings { - public static int constructor = 0x70a68512; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_peerNotifySettings extends PeerNotifySettings { - public static int constructor = 0x8d5e11ee; - - - public void readParams(AbsSerializedData stream) { - mute_until = stream.readInt32(); - sound = stream.readString(); - show_previews = stream.readBool(); - events_mask = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(mute_until); - stream.writeString(sound); - stream.writeBool(show_previews); - stream.writeInt32(events_mask); - } - } - - public static class TL_msg_resend_req extends TLObject { - public static int constructor = 0x7d861a08; - - public ArrayList msg_ids = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - msg_ids.add(stream.readInt64()); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = msg_ids.size(); - stream.writeInt32(count); - for (Long msg_id : msg_ids) { - stream.writeInt64(msg_id); - } - } - } - - public static class TL_http_wait extends TLObject { - public static int constructor = 0x9299359f; - - public int max_delay; - public int wait_after; - public int max_wait; - - public void readParams(AbsSerializedData stream) { - max_delay = stream.readInt32(); - wait_after = stream.readInt32(); - max_wait = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(max_delay); - stream.writeInt32(wait_after); - stream.writeInt32(max_wait); - } - } - - public static class contacts_Blocked extends TLObject { - public ArrayList blocked = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - public int count; - } - - public static class TL_contacts_blocked extends contacts_Blocked { - public static int constructor = 0x1c138d15; - - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - blocked.add((TL_contactBlocked)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 = blocked.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - blocked.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 TL_contacts_blockedSlice extends contacts_Blocked { - public static int constructor = 0x900802a1; - - - public void readParams(AbsSerializedData stream) { - count = stream.readInt32(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - blocked.add((TL_contactBlocked)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(count); - stream.writeInt32(0x1cb5c415); - int count = blocked.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - blocked.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 InputGeoPoint extends TLObject { - public double lat; - public double _long; - } - - public static class TL_inputGeoPoint extends InputGeoPoint { - public static int constructor = 0xf3b7acc9; - - - public void readParams(AbsSerializedData stream) { - lat = stream.readDouble(); - _long = stream.readDouble(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeDouble(lat); - stream.writeDouble(_long); - } - } - - public static class TL_inputGeoPointEmpty extends InputGeoPoint { - public static int constructor = 0xe4c123d6; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_help_inviteText extends TLObject { - public static int constructor = 0x18cb9f78; - - public String message; - - public void readParams(AbsSerializedData stream) { - message = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(message); - } - } - - public static class messages_DhConfig extends TLObject { - public byte[] random; - public int g; - public byte[] p; - public int version; - } - - public static class TL_messages_dhConfigNotModified extends messages_DhConfig { - public static int constructor = 0xc0e24635; - - - public void readParams(AbsSerializedData stream) { - random = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeByteArray(random); - } - } - - public static class TL_messages_dhConfig extends messages_DhConfig { - public static int constructor = 0x2c221edd; - - - public void readParams(AbsSerializedData stream) { - g = stream.readInt32(); - p = stream.readByteArray(); - version = stream.readInt32(); - random = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(g); - stream.writeByteArray(p); - stream.writeInt32(version); - stream.writeByteArray(random); - } - } - - public static class TL_audioEmpty extends Audio { - public static int constructor = 0x586988d8; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - } - } - - public static class TL_audio extends Audio { - public static int constructor = 0xc7ac6496; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - user_id = stream.readInt32(); - date = stream.readInt32(); - duration = stream.readInt32(); - mime_type = stream.readString(); - size = stream.readInt32(); - dc_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - stream.writeInt32(user_id); - stream.writeInt32(date); - stream.writeInt32(duration); - stream.writeString(mime_type); - stream.writeInt32(size); - stream.writeInt32(dc_id); - } - } - - public static class TL_destroy_sessions_res extends TLObject { - public static int constructor = 0xfb95abcd; - - public ArrayList destroy_results = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - destroy_results.add((DestroySessionRes)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - int count = destroy_results.size(); - stream.writeInt32(count); - for (DestroySessionRes destroy_result : destroy_results) { - destroy_result.serializeToStream(stream); - } - } - } - - public static class PrivacyRule extends TLObject { - public ArrayList users = new ArrayList<>(); - } - - 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 contacts = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - } - - public static class TL_contacts_contacts extends contacts_Contacts { - public static int constructor = 0x6f8b8cb2; - - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - contacts.add((TL_contact)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 = contacts.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - contacts.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 TL_contacts_contactsNotModified extends contacts_Contacts { - public static int constructor = 0xb74ba9d2; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - 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 photos = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - public int count; - } - - public static class TL_photos_photos extends photos_Photos { - public static int constructor = 0x8dca6aa5; - - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - photos.add((Photo)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 = photos.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - photos.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 TL_photos_photosSlice extends photos_Photos { - public static int constructor = 0x15051f54; - - - public void readParams(AbsSerializedData stream) { - count = stream.readInt32(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - photos.add((Photo)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(count); - stream.writeInt32(0x1cb5c415); - int count = photos.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - photos.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 TL_chatFull extends TLObject { - public static int constructor = 0x630e61be; - - public int id; - public ChatParticipants participants; - public Photo chat_photo; - public PeerNotifySettings notify_settings; - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - participants = (ChatParticipants)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - chat_photo = (Photo)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - notify_settings = (PeerNotifySettings)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - participants.serializeToStream(stream); - chat_photo.serializeToStream(stream); - notify_settings.serializeToStream(stream); - } - } - - public static class TL_msgs_all_info extends TLObject { - public static int constructor = 0x8cc0d131; - - public ArrayList msg_ids = new ArrayList<>(); - public String info; - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - msg_ids.add(stream.readInt64()); - } - info = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = msg_ids.size(); - stream.writeInt32(count); - for (Long msg_id : msg_ids) { - stream.writeInt64(msg_id); - } - stream.writeString(info); - } - } - - public static class TL_inputPeerNotifySettings extends TLObject { - public static int constructor = 0x46a2ce98; - - public int mute_until; - public String sound; - public boolean show_previews; - public int events_mask; - - public void readParams(AbsSerializedData stream) { - mute_until = stream.readInt32(); - sound = stream.readString(); - show_previews = stream.readBool(); - events_mask = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(mute_until); - stream.writeString(sound); - stream.writeBool(show_previews); - stream.writeInt32(events_mask); - } - } - - public static class TL_null extends TLObject { - public static int constructor = 0x56730bcc; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class InputUser extends TLObject { - public int user_id; - public long access_hash; - } - - public static class TL_inputUserSelf extends InputUser { - public static int constructor = 0xf7c1b13f; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputUserForeign extends InputUser { - public static int constructor = 0x655e74ff; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - access_hash = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - stream.writeInt64(access_hash); - } - } - - public static class TL_inputUserEmpty extends InputUser { - public static int constructor = 0xb98886cf; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputUserContact extends InputUser { - public static int constructor = 0x86e94f65; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - } - } - - public static class TL_p_q_inner_data extends TLObject { - public static int constructor = 0x83c95aec; - - public byte[] pq; - public byte[] p; - public byte[] q; - public byte[] nonce; - public byte[] server_nonce; - public byte[] new_nonce; - - public void readParams(AbsSerializedData stream) { - pq = stream.readByteArray(); - p = stream.readByteArray(); - q = stream.readByteArray(); - nonce = stream.readData(16); - server_nonce = stream.readData(16); - new_nonce = stream.readData(32); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeByteArray(pq); - stream.writeByteArray(p); - stream.writeByteArray(q); - stream.writeRaw(nonce); - stream.writeRaw(server_nonce); - stream.writeRaw(new_nonce); - } - } - - public static class TL_msgs_state_req extends TLObject { - public static int constructor = 0xda69fb52; - - public ArrayList msg_ids = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - msg_ids.add(stream.readInt64()); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = msg_ids.size(); - stream.writeInt32(count); - for (Long msg_id : msg_ids) { - stream.writeInt64(msg_id); - } - } - } - - public static class Bool extends TLObject { - } - - public static class TL_boolTrue extends Bool { - public static int constructor = 0x997275b5; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_boolFalse extends Bool { - public static int constructor = 0xbc799737; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_auth_exportedAuthorization extends TLObject { - public static int constructor = 0xdf969c2d; - - public int id; - public byte[] bytes; - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - bytes = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeByteArray(bytes); } } @@ -2133,15 +69,37 @@ public class TLRPC { public int embed_height; public int duration; public String author; + + public static WebPage TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + WebPage result = null; + switch(constructor) { + case 0xc586da1c: + result = new TL_webPagePending(); + break; + case 0xeb1477e8: + result = new TL_webPageEmpty(); + break; + case 0xa31ea0b5: + result = new TL_webPage(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in WebPage", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } } public static class TL_webPagePending extends WebPage { public static int constructor = 0xc586da1c; - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - date = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + date = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { @@ -2155,8 +113,8 @@ public class TLRPC { public static int constructor = 0xeb1477e8; - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); } public void serializeToStream(AbsSerializedData stream) { @@ -2165,57 +123,47 @@ public class TLRPC { } } - public static class TL_auth_passwordRecovery extends TLObject { - public static int constructor = 0x137948a5; - - public String email_pattern; - - public void readParams(AbsSerializedData stream) { - email_pattern = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(email_pattern); - } - } - public static class TL_webPage extends WebPage { public static int constructor = 0xa31ea0b5; - public void readParams(AbsSerializedData stream) { - flags = stream.readInt32(); - id = stream.readInt64(); - url = stream.readString(); - display_url = stream.readString(); + + public void readParams(AbsSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + id = stream.readInt64(exception); + url = stream.readString(exception); + display_url = stream.readString(exception); if ((flags & 1) != 0) { - type = stream.readString(); + type = stream.readString(exception); } if ((flags & 2) != 0) { - site_name = stream.readString(); + site_name = stream.readString(exception); } if ((flags & 4) != 0) { - title = stream.readString(); + title = stream.readString(exception); } if ((flags & 8) != 0) { - description = stream.readString(); + description = stream.readString(exception); } if ((flags & 16) != 0) { - photo = (Photo) TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); } if ((flags & 32) != 0) { - embed_url = stream.readString(); - embed_type = stream.readString(); + embed_url = stream.readString(exception); + } + if ((flags & 32) != 0) { + embed_type = stream.readString(exception); } if ((flags & 64) != 0) { - embed_width = stream.readInt32(); - embed_height = stream.readInt32(); + embed_width = stream.readInt32(exception); + } + if ((flags & 64) != 0) { + embed_height = stream.readInt32(exception); } if ((flags & 128) != 0) { - duration = stream.readInt32(); + duration = stream.readInt32(exception); } if ((flags & 256) != 0) { - author = stream.readString(); + author = stream.readString(exception); } } @@ -2242,10 +190,14 @@ public class TLRPC { } if ((flags & 32) != 0) { stream.writeString(embed_url); + } + if ((flags & 32) != 0) { stream.writeString(embed_type); } if ((flags & 64) != 0) { stream.writeInt32(embed_width); + } + if ((flags & 64) != 0) { stream.writeInt32(embed_height); } if ((flags & 128) != 0) { @@ -2257,479 +209,229 @@ public class TLRPC { } } - public static class InputNotifyPeer extends TLObject { - } + public static class TL_inputPeerNotifySettings extends TLObject { + public static int constructor = 0x46a2ce98; - public static class TL_inputNotifyChats extends InputNotifyPeer { - public static int constructor = 0x4a95e84e; + public int mute_until; + public String sound; + public boolean show_previews; + public int events_mask; - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); + public static TL_inputPeerNotifySettings TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_inputPeerNotifySettings.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_inputPeerNotifySettings", constructor)); + } else { + return null; + } + } + TL_inputPeerNotifySettings result = new TL_inputPeerNotifySettings(); + result.readParams(stream, exception); + return result; } - } - public static class TL_inputNotifyPeer extends InputNotifyPeer { - public static int constructor = 0xb8bc5b0c; - - public InputPeer peer; - - public void readParams(AbsSerializedData stream) { - peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public void readParams(AbsSerializedData stream, boolean exception) { + mute_until = stream.readInt32(exception); + sound = stream.readString(exception); + show_previews = stream.readBool(exception); + events_mask = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - peer.serializeToStream(stream); + stream.writeInt32(mute_until); + stream.writeString(sound); + stream.writeBool(show_previews); + stream.writeInt32(events_mask); } } - public static class TL_inputNotifyUsers extends InputNotifyPeer { - public static int constructor = 0x193b4417; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputNotifyGeoChatPeer extends InputNotifyPeer { - public static int constructor = 0x4d8ddec8; - - public TL_inputGeoChat peer; - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - peer.serializeToStream(stream); - } - } - - public static class TL_inputNotifyAll extends InputNotifyPeer { - public static int constructor = 0xa429b886; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class InputFileLocation extends TLObject { + public static class InputEncryptedFile extends TLObject { public long id; public long access_hash; - public long volume_id; - public int local_id; - public long secret; - } + public int parts; + public int key_fingerprint; + public String md5_checksum; - public static class TL_inputAudioFileLocation extends InputFileLocation { - public static int constructor = 0x74dc404d; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - } - } - - public static class TL_inputEncryptedFileLocation extends InputFileLocation { - public static int constructor = 0xf5235d55; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - } - } - - public static class TL_inputVideoFileLocation extends InputFileLocation { - public static int constructor = 0x3d0364ec; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - } - } - - public static class TL_inputDocumentFileLocation extends InputFileLocation { - public static int constructor = 0x4e45abe9; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - } - } - - public static class TL_inputFileLocation extends InputFileLocation { - public static int constructor = 0x14637196; - - - public void readParams(AbsSerializedData stream) { - volume_id = stream.readInt64(); - local_id = stream.readInt32(); - secret = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(volume_id); - stream.writeInt32(local_id); - stream.writeInt64(secret); - } - } - - public static class TL_photos_photo extends TLObject { - public static int constructor = 0x20212ca8; - - public Photo photo; - public ArrayList users = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - photo = (Photo)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add((User) TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + public static InputEncryptedFile TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputEncryptedFile result = null; + switch(constructor) { + case 0x5a17b5e5: + result = new TL_inputEncryptedFile(); + break; + case 0x2dc173c8: + result = new TL_inputEncryptedFileBigUploaded(); + break; + case 0x1837c364: + result = new TL_inputEncryptedFileEmpty(); + break; + case 0x64bd0306: + result = new TL_inputEncryptedFileUploaded(); + break; } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - photo.serializeToStream(stream); - stream.writeInt32(0x1cb5c415); - int count = users.size(); - stream.writeInt32(count); - for (User user : users) { - user.serializeToStream(stream); + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputEncryptedFile", constructor)); } + if (result != null) { + result.readParams(stream, exception); + } + return result; } } - public static class TL_userContact extends User { - public static int constructor = 0xcab35e18; + public static class TL_inputEncryptedFile extends InputEncryptedFile { + public static int constructor = 0x5a17b5e5; - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - first_name = stream.readString(); - last_name = stream.readString(); - username = stream.readString(); - access_hash = stream.readInt64(); - phone = stream.readString(); - photo = (UserProfilePhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeString(username); + stream.writeInt64(id); stream.writeInt64(access_hash); - stream.writeString(phone); - photo.serializeToStream(stream); - status.serializeToStream(stream); } } - public static class TL_userRequest extends User { - public static int constructor = 0xd9ccc4ef; + public static class TL_inputEncryptedFileBigUploaded extends InputEncryptedFile { + public static int constructor = 0x2dc173c8; - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - first_name = stream.readString(); - last_name = stream.readString(); - username = stream.readString(); - access_hash = stream.readInt64(); - phone = stream.readString(); - photo = (UserProfilePhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + parts = stream.readInt32(exception); + key_fingerprint = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeString(username); - stream.writeInt64(access_hash); - stream.writeString(phone); - photo.serializeToStream(stream); - status.serializeToStream(stream); + stream.writeInt64(id); + stream.writeInt32(parts); + stream.writeInt32(key_fingerprint); } } - public static class TL_userForeign extends User { - public static int constructor = 0x75cf7a8; + public static class TL_inputEncryptedFileEmpty extends InputEncryptedFile { + public static int constructor = 0x1837c364; - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - first_name = stream.readString(); - last_name = stream.readString(); - username = stream.readString(); - access_hash = stream.readInt64(); - photo = (UserProfilePhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputEncryptedFileUploaded extends InputEncryptedFile { + public static int constructor = 0x64bd0306; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + parts = stream.readInt32(exception); + md5_checksum = stream.readString(exception); + key_fingerprint = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeString(username); - stream.writeInt64(access_hash); - photo.serializeToStream(stream); - status.serializeToStream(stream); + stream.writeInt64(id); + stream.writeInt32(parts); + stream.writeString(md5_checksum); + stream.writeInt32(key_fingerprint); } } - public static class TL_userDeleted extends User { - public static int constructor = 0xd6016d7a; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - first_name = stream.readString(); - last_name = stream.readString(); - username = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeString(username); - } - } - - public static class TL_userSelf extends User { - public static int constructor = 0x1c60e608; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - first_name = stream.readString(); - last_name = stream.readString(); - username = stream.readString(); - phone = stream.readString(); - photo = (UserProfilePhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeString(username); - stream.writeString(phone); - photo.serializeToStream(stream); - status.serializeToStream(stream); - } - } - - public static class GeoChatMessage extends TLObject { - public int chat_id; - public int id; - public int from_id; - public int date; - public String message; - public MessageMedia media; - public MessageAction action; - } - - public static class TL_geoChatMessage extends GeoChatMessage { - public static int constructor = 0x4505f8e1; - - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - id = stream.readInt32(); - from_id = stream.readInt32(); - date = stream.readInt32(); - message = stream.readString(); - media = (MessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - stream.writeInt32(id); - stream.writeInt32(from_id); - stream.writeInt32(date); - stream.writeString(message); - media.serializeToStream(stream); - } - } - - public static class TL_geoChatMessageService extends GeoChatMessage { - public static int constructor = 0xd34fa24e; - - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - id = stream.readInt32(); - from_id = stream.readInt32(); - date = stream.readInt32(); - action = (MessageAction)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - stream.writeInt32(id); - stream.writeInt32(from_id); - stream.writeInt32(date); - action.serializeToStream(stream); - } - } - - public static class TL_geoChatMessageEmpty extends GeoChatMessage { - public static int constructor = 0x60311a9b; - - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - stream.writeInt32(id); - } - } - - public static class TL_pong extends TLObject { - public static int constructor = 0x347773c5; - + public static class MsgDetailedInfo extends TLObject { + public long answer_msg_id; + public int bytes; + public int status; public long msg_id; - public long ping_id; - public void readParams(AbsSerializedData stream) { - msg_id = stream.readInt64(); - ping_id = stream.readInt64(); + public static MsgDetailedInfo TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + MsgDetailedInfo result = null; + switch(constructor) { + case 0x809db6df: + result = new TL_msg_new_detailed_info(); + break; + case 0x276d3ec6: + result = new TL_msg_detailed_info(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in MsgDetailedInfo", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_msg_new_detailed_info extends MsgDetailedInfo { + public static int constructor = 0x809db6df; + + + public void readParams(AbsSerializedData stream, boolean exception) { + answer_msg_id = stream.readInt64(exception); + bytes = stream.readInt32(exception); + status = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(answer_msg_id); + stream.writeInt32(bytes); + stream.writeInt32(status); + } + } + + public static class TL_msg_detailed_info extends MsgDetailedInfo { + public static int constructor = 0x276d3ec6; + + + public void readParams(AbsSerializedData stream, boolean exception) { + msg_id = stream.readInt64(exception); + answer_msg_id = stream.readInt64(exception); + bytes = stream.readInt32(exception); + status = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(msg_id); - stream.writeInt64(ping_id); + stream.writeInt64(answer_msg_id); + stream.writeInt32(bytes); + stream.writeInt32(status); } } - public static class TL_messageActionChatEditPhoto extends MessageAction { - public static int constructor = 0x7fcb13a8; + public static class GeoPoint extends TLObject { + public double _long; + public double lat; - - public void readParams(AbsSerializedData stream) { - photo = (Photo)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - photo.serializeToStream(stream); - } - } - - public static class TL_messageActionChatDeleteUser extends MessageAction { - public static int constructor = 0xb2ae9b0c; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - } - } - - public static class TL_messageActionChatDeletePhoto extends MessageAction { - public static int constructor = 0x95e3fbef; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_messageActionChatAddUser extends MessageAction { - public static int constructor = 0x5e3cfc4b; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - } - } - - public static class TL_messageActionChatCreate extends MessageAction { - public static int constructor = 0xa6638b9a; - - - public void readParams(AbsSerializedData stream) { - title = stream.readString(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add(stream.readInt32()); + public static GeoPoint TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + GeoPoint result = null; + switch(constructor) { + case 0x1117dd5f: + result = new TL_geoPointEmpty(); + break; + case 0x2049d70c: + result = new TL_geoPoint(); + break; } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(title); - stream.writeInt32(0x1cb5c415); - int count = users.size(); - stream.writeInt32(count); - for (Integer user : users) { - stream.writeInt32(user); + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in GeoPoint", constructor)); } + if (result != null) { + result.readParams(stream, exception); + } + return result; } } - public static class TL_messageActionEmpty extends MessageAction { - public static int constructor = 0xb6aef7b0; + public static class TL_geoPointEmpty extends GeoPoint { + public static int constructor = 0x1117dd5f; public void serializeToStream(AbsSerializedData stream) { @@ -2737,307 +439,244 @@ public class TLRPC { } } - public static class TL_messageActionChatEditTitle extends MessageAction { - public static int constructor = 0xb5a1ce5a; + public static class TL_geoPoint extends GeoPoint { + public static int constructor = 0x2049d70c; - public void readParams(AbsSerializedData stream) { - title = stream.readString(); + public void readParams(AbsSerializedData stream, boolean exception) { + _long = stream.readDouble(exception); + lat = stream.readDouble(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeString(title); + stream.writeDouble(_long); + stream.writeDouble(lat); } } - public static class TL_messageActionGeoChatCreate extends MessageAction { - public static int constructor = 0x6f038ebc; + public static class TL_accountDaysTTL extends TLObject { + public static int constructor = 0xb8d0afdf; + public int days; - public void readParams(AbsSerializedData stream) { - title = stream.readString(); - address = stream.readString(); + public static TL_accountDaysTTL TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_accountDaysTTL.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_accountDaysTTL", constructor)); + } else { + return null; + } + } + TL_accountDaysTTL result = new TL_accountDaysTTL(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + days = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeString(title); - stream.writeString(address); + stream.writeInt32(days); } } - public static class TL_messageActionGeoChatCheckin extends MessageAction { - public static int constructor = 0xc7d53de; + public static class TL_error extends TLObject { + public static int constructor = 0xc4b9f9bb; + public int code; + public String text; - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); + public static TL_error TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_error.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_error", constructor)); + } else { + return null; + } + } + TL_error result = new TL_error(); + result.readParams(stream, exception); + return result; } - } - public static class Set_client_DH_params_answer extends TLObject { - public byte[] nonce; - public byte[] server_nonce; - public byte[] new_nonce_hash2; - public byte[] new_nonce_hash3; - public byte[] new_nonce_hash1; - } - - public static class TL_dh_gen_retry extends Set_client_DH_params_answer { - public static int constructor = 0x46dc1fb9; - - - public void readParams(AbsSerializedData stream) { - nonce = stream.readData(16); - server_nonce = stream.readData(16); - new_nonce_hash2 = stream.readData(16); + public void readParams(AbsSerializedData stream, boolean exception) { + code = stream.readInt32(exception); + text = stream.readString(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeRaw(nonce); - stream.writeRaw(server_nonce); - stream.writeRaw(new_nonce_hash2); + stream.writeInt32(code); + stream.writeString(text); } } - public static class TL_dh_gen_fail extends Set_client_DH_params_answer { - public static int constructor = 0xa69dae02; + public static class TL_messageEmpty extends Message { + public static int constructor = 0x83e5de54; - public void readParams(AbsSerializedData stream) { - nonce = stream.readData(16); - server_nonce = stream.readData(16); - new_nonce_hash3 = stream.readData(16); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeRaw(nonce); - stream.writeRaw(server_nonce); - stream.writeRaw(new_nonce_hash3); + stream.writeInt32(id); } } - public static class TL_dh_gen_ok extends Set_client_DH_params_answer { - public static int constructor = 0x3bcbf734; + public static class TL_messageService extends Message { + public static int constructor = 0x1d86f70e; - public void readParams(AbsSerializedData stream) { - nonce = stream.readData(16); - server_nonce = stream.readData(16); - new_nonce_hash1 = stream.readData(16); + public void readParams(AbsSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + id = stream.readInt32(exception); + from_id = stream.readInt32(exception); + to_id = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + date = stream.readInt32(exception); + action = MessageAction.TLdeserialize(stream, stream.readInt32(exception), exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeRaw(nonce); - stream.writeRaw(server_nonce); - stream.writeRaw(new_nonce_hash1); - } - } - - public static class PeerNotifyEvents extends TLObject { - } - - public static class TL_peerNotifyEventsEmpty extends PeerNotifyEvents { - public static int constructor = 0xadd53cb3; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_peerNotifyEventsAll extends PeerNotifyEvents { - public static int constructor = 0x6d1ded88; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_chatLocated extends TLObject { - public static int constructor = 0x3631cf4c; - - public int chat_id; - public int distance; - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - distance = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - stream.writeInt32(distance); - } - } - - public static class DecryptedMessage extends TLObject { - public long random_id; - public byte[] random_bytes; - public DecryptedMessageAction action; - public int ttl; - public String message; - public DecryptedMessageMedia media; - } - - public static class TL_decryptedMessageService extends DecryptedMessage { - public static int constructor = 0x73164160; - - - public void readParams(AbsSerializedData stream) { - random_id = stream.readInt64(); - action = (DecryptedMessageAction)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(random_id); + stream.writeInt32(flags); + stream.writeInt32(id); + stream.writeInt32(from_id); + to_id.serializeToStream(stream); + stream.writeInt32(date); action.serializeToStream(stream); } } - public static class TL_decryptedMessage extends DecryptedMessage { - public static int constructor = 0x204d3878; + public static class TL_messages_stickerSet extends TLObject { + public static int constructor = 0xb60a24a6; + public TL_stickerSet set; + public ArrayList packs = new ArrayList<>(); + public ArrayList documents = new ArrayList<>(); - public void readParams(AbsSerializedData stream) { - random_id = stream.readInt64(); - ttl = stream.readInt32(); - message = stream.readString(); - media = (DecryptedMessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public static TL_messages_stickerSet TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_messages_stickerSet.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_messages_stickerSet", constructor)); + } else { + return null; + } + } + TL_messages_stickerSet result = new TL_messages_stickerSet(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + set = TL_stickerSet.TLdeserialize(stream, stream.readInt32(exception), exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_stickerPack object = TL_stickerPack.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + packs.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Document object = Document.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + documents.add(object); + } } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt64(random_id); - stream.writeInt32(ttl); - stream.writeString(message); - media.serializeToStream(stream); + set.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = packs.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + packs.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = documents.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + documents.get(a).serializeToStream(stream); + } } } - public static class InputPeerNotifyEvents extends TLObject { - } - - public static class TL_inputPeerNotifyEventsAll extends InputPeerNotifyEvents { - public static int constructor = 0xe86a2c74; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputPeerNotifyEventsEmpty extends InputPeerNotifyEvents { - public static int constructor = 0xf03064d8; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_client_DH_inner_data extends TLObject { - public static int constructor = 0x6643b654; - - public byte[] nonce; - public byte[] server_nonce; - public long retry_id; - public byte[] g_b; - - public void readParams(AbsSerializedData stream) { - nonce = stream.readData(16); - server_nonce = stream.readData(16); - retry_id = stream.readInt64(); - g_b = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeRaw(nonce); - stream.writeRaw(server_nonce); - stream.writeInt64(retry_id); - stream.writeByteArray(g_b); - } - } - - public static class TL_video extends Video { - public static int constructor = 0x388fa391; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - user_id = stream.readInt32(); - date = stream.readInt32(); - caption = stream.readString(); - duration = stream.readInt32(); - mime_type = stream.readString(); - size = stream.readInt32(); - thumb = (PhotoSize)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - dc_id = stream.readInt32(); - w = stream.readInt32(); - h = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - stream.writeInt32(user_id); - stream.writeInt32(date); - stream.writeString(caption); - stream.writeInt32(duration); - stream.writeString(mime_type); - stream.writeInt32(size); - thumb.serializeToStream(stream); - stream.writeInt32(dc_id); - stream.writeInt32(w); - stream.writeInt32(h); - } - } - - public static class TL_videoEmpty extends Video { - public static int constructor = 0xc10658a8; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - } - } - - public static class TL_contactBlocked extends TLObject { - public static int constructor = 0x561bc879; + public static class TL_contactStatus extends TLObject { + public static int constructor = 0xd3680c61; public int user_id; - public int date; + public UserStatus status; - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - date = stream.readInt32(); + public static TL_contactStatus TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_contactStatus.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_contactStatus", constructor)); + } else { + return null; + } + } + TL_contactStatus result = new TL_contactStatus(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + status = UserStatus.TLdeserialize(stream, stream.readInt32(exception), exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(user_id); - stream.writeInt32(date); + status.serializeToStream(stream); } } public static class InputDocument extends TLObject { public long id; public long access_hash; + + public static InputDocument TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputDocument result = null; + switch(constructor) { + case 0x72f0eaae: + result = new TL_inputDocumentEmpty(); + break; + case 0x18798952: + result = new TL_inputDocument(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputDocument", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } } public static class TL_inputDocumentEmpty extends InputDocument { @@ -3053,9 +692,9 @@ public class TLRPC { public static int constructor = 0x18798952; - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); } public void serializeToStream(AbsSerializedData stream) { @@ -3065,93 +704,243 @@ public class TLRPC { } } - public static class TL_inputAppEvent extends TLObject { - public static int constructor = 0x770656a8; + public static class TL_auth_authorization extends TLObject { + public static int constructor = 0xf6b673a4; - public double time; - public String type; - public long peer; - public String data; + public int expires; + public User user; - public void readParams(AbsSerializedData stream) { - time = stream.readDouble(); - type = stream.readString(); - peer = stream.readInt64(); - data = stream.readString(); + public static TL_auth_authorization TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_auth_authorization.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_auth_authorization", constructor)); + } else { + return null; + } + } + TL_auth_authorization result = new TL_auth_authorization(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + expires = stream.readInt32(exception); + user = User.TLdeserialize(stream, stream.readInt32(exception), exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeDouble(time); - stream.writeString(type); - stream.writeInt64(peer); - stream.writeString(data); + stream.writeInt32(expires); + user.serializeToStream(stream); } } - public static class TL_messages_affectedHistory extends TLObject { - public static int constructor = 0xb45c69d1; + public static class Set_client_DH_params_answer extends TLObject { + public byte[] nonce; + public byte[] server_nonce; + public byte[] new_nonce_hash2; + public byte[] new_nonce_hash3; + public byte[] new_nonce_hash1; - public int pts; - public int pts_count; - public int offset; + public static Set_client_DH_params_answer TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + Set_client_DH_params_answer result = null; + switch(constructor) { + case 0x46dc1fb9: + result = new TL_dh_gen_retry(); + break; + case 0xa69dae02: + result = new TL_dh_gen_fail(); + break; + case 0x3bcbf734: + result = new TL_dh_gen_ok(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in Set_client_DH_params_answer", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } - public void readParams(AbsSerializedData stream) { - pts = stream.readInt32(); - pts_count = stream.readInt32(); - offset = stream.readInt32(); + public static class TL_dh_gen_retry extends Set_client_DH_params_answer { + public static int constructor = 0x46dc1fb9; + + + public void readParams(AbsSerializedData stream, boolean exception) { + nonce = stream.readData(16, exception); + server_nonce = stream.readData(16, exception); + new_nonce_hash2 = stream.readData(16, exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(pts); - stream.writeInt32(pts_count); - stream.writeInt32(offset); + stream.writeRaw(nonce); + stream.writeRaw(server_nonce); + stream.writeRaw(new_nonce_hash2); } } - public static class TL_documentEmpty extends Document { - public static int constructor = 0x36f8c871; + public static class TL_dh_gen_fail extends Set_client_DH_params_answer { + public static int constructor = 0xa69dae02; - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); + public void readParams(AbsSerializedData stream, boolean exception) { + nonce = stream.readData(16, exception); + server_nonce = stream.readData(16, exception); + new_nonce_hash3 = stream.readData(16, exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt64(id); + stream.writeRaw(nonce); + stream.writeRaw(server_nonce); + stream.writeRaw(new_nonce_hash3); } } - public static class TL_document extends Document { - public static int constructor = 0xf9a39f4f; + public static class TL_dh_gen_ok extends Set_client_DH_params_answer { + public static int constructor = 0x3bcbf734; - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - date = stream.readInt32(); - mime_type = stream.readString(); - size = stream.readInt32(); - thumb = (PhotoSize)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - dc_id = stream.readInt32(); - stream.readInt32(); - int count = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + nonce = stream.readData(16, exception); + server_nonce = stream.readData(16, exception); + new_nonce_hash1 = stream.readData(16, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeRaw(nonce); + stream.writeRaw(server_nonce); + stream.writeRaw(new_nonce_hash1); + } + } + + public static class InputMedia extends TLObject { + public String phone_number; + public String first_name; + public String last_name; + public InputFile file; + public InputFile thumb; + public String mime_type; + public ArrayList attributes = new ArrayList<>(); + public String caption; + public InputGeoPoint geo_point; + public int duration; + public int w; + public int h; + public String title; + public String address; + public String provider; + public String venue_id; + + public static InputMedia TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputMedia result = null; + switch(constructor) { + case 0xa6e45987: + result = new TL_inputMediaContact(); + break; + case 0x41481486: + result = new TL_inputMediaUploadedThumbDocument(); + break; + case 0x89938781: + result = new TL_inputMediaAudio(); + break; + case 0xd184e841: + result = new TL_inputMediaDocument(); + break; + case 0x936a4ebd: + result = new TL_inputMediaVideo(); + break; + case 0xf9c44144: + result = new TL_inputMediaGeoPoint(); + break; + case 0x9664f57f: + result = new TL_inputMediaEmpty(); + break; + case 0x96fb97dc: + result = new TL_inputMediaUploadedThumbVideo(); + break; + case 0xf7aff1c0: + result = new TL_inputMediaUploadedPhoto(); + break; + case 0x2827a81a: + result = new TL_inputMediaVenue(); + break; + case 0x4e498cab: + result = new TL_inputMediaUploadedAudio(); + break; + case 0xe13fd4bc: + result = new TL_inputMediaUploadedVideo(); + break; + case 0xffe76b78: + result = new TL_inputMediaUploadedDocument(); + break; + case 0xe9bfb4f3: + result = new TL_inputMediaPhoto(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputMedia", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inputMediaContact extends InputMedia { + public static int constructor = 0xa6e45987; + + + public void readParams(AbsSerializedData stream, boolean exception) { + phone_number = stream.readString(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(phone_number); + stream.writeString(first_name); + stream.writeString(last_name); + } + } + + public static class TL_inputMediaUploadedThumbDocument extends InputMedia { + public static int constructor = 0x41481486; + + + public void readParams(AbsSerializedData stream, boolean exception) { + file = InputFile.TLdeserialize(stream, stream.readInt32(exception), exception); + thumb = InputFile.TLdeserialize(stream, stream.readInt32(exception), exception); + mime_type = stream.readString(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - attributes.add((DocumentAttribute)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + DocumentAttribute object = DocumentAttribute.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + attributes.add(object); } } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - stream.writeInt32(date); - stream.writeString(mime_type); - stream.writeInt32(size); + file.serializeToStream(stream); thumb.serializeToStream(stream); - stream.writeInt32(dc_id); + stream.writeString(mime_type); stream.writeInt32(0x1cb5c415); int count = attributes.size(); stream.writeInt32(count); @@ -3161,11 +950,69 @@ public class TLRPC { } } - public static class ContactLink extends TLObject { + public static class TL_inputMediaAudio extends InputMedia { + public static int constructor = 0x89938781; + + public InputAudio id; + + public void readParams(AbsSerializedData stream, boolean exception) { + id = InputAudio.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + id.serializeToStream(stream); + } } - public static class TL_contactLinkNone extends ContactLink { - public static int constructor = 0xfeedd3ad; + public static class TL_inputMediaDocument extends InputMedia { + public static int constructor = 0xd184e841; + + public InputDocument id; + + public void readParams(AbsSerializedData stream, boolean exception) { + id = InputDocument.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + id.serializeToStream(stream); + } + } + + public static class TL_inputMediaVideo extends InputMedia { + public static int constructor = 0x936a4ebd; + + public InputVideo id; + + public void readParams(AbsSerializedData stream, boolean exception) { + id = InputVideo.TLdeserialize(stream, stream.readInt32(exception), exception); + caption = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + id.serializeToStream(stream); + stream.writeString(caption); + } + } + + public static class TL_inputMediaGeoPoint extends InputMedia { + public static int constructor = 0xf9c44144; + + + public void readParams(AbsSerializedData stream, boolean exception) { + geo_point = InputGeoPoint.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + geo_point.serializeToStream(stream); + } + } + + public static class TL_inputMediaEmpty extends InputMedia { + public static int constructor = 0x9664f57f; public void serializeToStream(AbsSerializedData stream) { @@ -3173,46 +1020,555 @@ public class TLRPC { } } - public static class TL_contactLinkContact extends ContactLink { - public static int constructor = 0xd502c2d0; + public static class TL_inputMediaUploadedThumbVideo extends InputMedia { + public static int constructor = 0x96fb97dc; + public void readParams(AbsSerializedData stream, boolean exception) { + file = InputFile.TLdeserialize(stream, stream.readInt32(exception), exception); + thumb = InputFile.TLdeserialize(stream, stream.readInt32(exception), exception); + duration = stream.readInt32(exception); + w = stream.readInt32(exception); + h = stream.readInt32(exception); + caption = stream.readString(exception); + } + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); + file.serializeToStream(stream); + thumb.serializeToStream(stream); + stream.writeInt32(duration); + stream.writeInt32(w); + stream.writeInt32(h); + stream.writeString(caption); } } - public static class TL_contactLinkHasPhone extends ContactLink { - public static int constructor = 0x268f3f59; + public static class TL_inputMediaUploadedPhoto extends InputMedia { + public static int constructor = 0xf7aff1c0; + public void readParams(AbsSerializedData stream, boolean exception) { + file = InputFile.TLdeserialize(stream, stream.readInt32(exception), exception); + caption = stream.readString(exception); + } + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); + file.serializeToStream(stream); + stream.writeString(caption); } } - public static class TL_contactLinkUnknown extends ContactLink { - public static int constructor = 0x5f4f9247; + public static class TL_inputMediaVenue extends InputMedia { + public static int constructor = 0x2827a81a; + public void readParams(AbsSerializedData stream, boolean exception) { + geo_point = InputGeoPoint.TLdeserialize(stream, stream.readInt32(exception), exception); + title = stream.readString(exception); + address = stream.readString(exception); + provider = stream.readString(exception); + venue_id = stream.readString(exception); + } + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); + geo_point.serializeToStream(stream); + stream.writeString(title); + stream.writeString(address); + stream.writeString(provider); + stream.writeString(venue_id); + } + } + + public static class TL_inputMediaUploadedAudio extends InputMedia { + public static int constructor = 0x4e498cab; + + + public void readParams(AbsSerializedData stream, boolean exception) { + file = InputFile.TLdeserialize(stream, stream.readInt32(exception), exception); + duration = stream.readInt32(exception); + mime_type = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + file.serializeToStream(stream); + stream.writeInt32(duration); + stream.writeString(mime_type); + } + } + + public static class TL_inputMediaUploadedVideo extends InputMedia { + public static int constructor = 0xe13fd4bc; + + + public void readParams(AbsSerializedData stream, boolean exception) { + file = InputFile.TLdeserialize(stream, stream.readInt32(exception), exception); + duration = stream.readInt32(exception); + w = stream.readInt32(exception); + h = stream.readInt32(exception); + caption = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + file.serializeToStream(stream); + stream.writeInt32(duration); + stream.writeInt32(w); + stream.writeInt32(h); + stream.writeString(caption); + } + } + + public static class TL_inputMediaUploadedDocument extends InputMedia { + public static int constructor = 0xffe76b78; + + + public void readParams(AbsSerializedData stream, boolean exception) { + file = InputFile.TLdeserialize(stream, stream.readInt32(exception), exception); + mime_type = stream.readString(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + DocumentAttribute object = DocumentAttribute.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + attributes.add(object); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + file.serializeToStream(stream); + stream.writeString(mime_type); + stream.writeInt32(0x1cb5c415); + int count = attributes.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + attributes.get(a).serializeToStream(stream); + } + } + } + + public static class TL_inputMediaPhoto extends InputMedia { + public static int constructor = 0xe9bfb4f3; + + public InputPhoto id; + + public void readParams(AbsSerializedData stream, boolean exception) { + id = InputPhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + caption = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + id.serializeToStream(stream); + stream.writeString(caption); + } + } + + public static class TL_geochats_statedMessage extends TLObject { + public static int constructor = 0x17b1578b; + + public GeoChatMessage message; + public ArrayList chats = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + public int seq; + + public static TL_geochats_statedMessage TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_geochats_statedMessage.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_geochats_statedMessage", constructor)); + } else { + return null; + } + } + TL_geochats_statedMessage result = new TL_geochats_statedMessage(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + message = GeoChatMessage.TLdeserialize(stream, stream.readInt32(exception), exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Chat object = Chat.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + chats.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + seq = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + message.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.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); + } + stream.writeInt32(seq); + } + } + + public static class messages_Dialogs extends TLObject { + public ArrayList dialogs = new ArrayList<>(); + public ArrayList messages = new ArrayList<>(); + public ArrayList chats = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + public int count; + + public static messages_Dialogs TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + messages_Dialogs result = null; + switch(constructor) { + case 0x15ba6c40: + result = new TL_messages_dialogs(); + break; + case 0x71e094f3: + result = new TL_messages_dialogsSlice(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in messages_Dialogs", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_messages_dialogs extends messages_Dialogs { + public static int constructor = 0x15ba6c40; + + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_dialog object = TL_dialog.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + dialogs.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Message object = Message.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + messages.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Chat object = Chat.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + chats.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = dialogs.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + dialogs.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = messages.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + messages.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.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 TL_messages_dialogsSlice extends messages_Dialogs { + public static int constructor = 0x71e094f3; + + + public void readParams(AbsSerializedData stream, boolean exception) { + count = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_dialog object = TL_dialog.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + dialogs.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Message object = Message.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + messages.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Chat object = Chat.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + chats.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(count); + stream.writeInt32(0x1cb5c415); + int count = dialogs.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + dialogs.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = messages.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + messages.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.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 TL_server_DH_inner_data extends TLObject { + public static int constructor = 0xb5890dba; + + public byte[] nonce; + public byte[] server_nonce; + public int g; + public byte[] dh_prime; + public byte[] g_a; + public int server_time; + + public static TL_server_DH_inner_data TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_server_DH_inner_data.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_server_DH_inner_data", constructor)); + } else { + return null; + } + } + TL_server_DH_inner_data result = new TL_server_DH_inner_data(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + nonce = stream.readData(16, exception); + server_nonce = stream.readData(16, exception); + g = stream.readInt32(exception); + dh_prime = stream.readByteArray(exception); + g_a = stream.readByteArray(exception); + server_time = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeRaw(nonce); + stream.writeRaw(server_nonce); + stream.writeInt32(g); + stream.writeByteArray(dh_prime); + stream.writeByteArray(g_a); + stream.writeInt32(server_time); } } public static class InputPrivacyRule extends TLObject { public ArrayList users = new ArrayList<>(); + + public static InputPrivacyRule TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputPrivacyRule result = null; + switch(constructor) { + case 0x90110467: + result = new TL_inputPrivacyValueDisallowUsers(); + break; + case 0xd66b66c9: + result = new TL_inputPrivacyValueDisallowAll(); + break; + case 0xba52007: + result = new TL_inputPrivacyValueDisallowContacts(); + break; + case 0x184b35ce: + result = new TL_inputPrivacyValueAllowAll(); + break; + case 0xd09e07b: + result = new TL_inputPrivacyValueAllowContacts(); + break; + case 0x131cc67f: + result = new TL_inputPrivacyValueAllowUsers(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputPrivacyRule", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } } public static class TL_inputPrivacyValueDisallowUsers extends InputPrivacyRule { public static int constructor = 0x90110467; - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - users.add((InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + InputUser object = InputUser.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); } } @@ -3221,8 +1577,8 @@ public class TLRPC { stream.writeInt32(0x1cb5c415); int count = users.size(); stream.writeInt32(count); - for (InputUser user : users) { - user.serializeToStream(stream); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); } } } @@ -3267,11 +1623,21 @@ public class TLRPC { public static int constructor = 0x131cc67f; - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - users.add((InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + InputUser object = InputUser.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); } } @@ -3280,34 +1646,1265 @@ public class TLRPC { stream.writeInt32(0x1cb5c415); int count = users.size(); stream.writeInt32(count); - for (InputUser user : users) { - user.serializeToStream(stream); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); } } } - public static class InputMedia extends TLObject { + public static class TL_contacts_link extends TLObject { + public static int constructor = 0x3ace484c; + + public ContactLink my_link; + public ContactLink foreign_link; + public User user; + + public static TL_contacts_link TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_contacts_link.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_contacts_link", constructor)); + } else { + return null; + } + } + TL_contacts_link result = new TL_contacts_link(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + my_link = ContactLink.TLdeserialize(stream, stream.readInt32(exception), exception); + foreign_link = ContactLink.TLdeserialize(stream, stream.readInt32(exception), exception); + user = User.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + my_link.serializeToStream(stream); + foreign_link.serializeToStream(stream); + user.serializeToStream(stream); + } + } + + public static class photos_Photos extends TLObject { + public ArrayList photos = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + public int count; + + public static photos_Photos TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + photos_Photos result = null; + switch(constructor) { + case 0x8dca6aa5: + result = new TL_photos_photos(); + break; + case 0x15051f54: + result = new TL_photos_photosSlice(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in photos_Photos", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_photos_photos extends photos_Photos { + public static int constructor = 0x8dca6aa5; + + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Photo object = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + photos.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = photos.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + photos.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 TL_photos_photosSlice extends photos_Photos { + public static int constructor = 0x15051f54; + + + public void readParams(AbsSerializedData stream, boolean exception) { + count = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Photo object = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + photos.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(count); + stream.writeInt32(0x1cb5c415); + int count = photos.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + photos.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 TL_msgs_ack extends TLObject { + public static int constructor = 0x62d6b459; + + public ArrayList msg_ids = new ArrayList<>(); + + public static TL_msgs_ack TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_msgs_ack.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_msgs_ack", constructor)); + } else { + return null; + } + } + TL_msgs_ack result = new TL_msgs_ack(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + msg_ids.add(stream.readInt64(exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = msg_ids.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt64(msg_ids.get(a)); + } + } + } + + public static class UserStatus extends TLObject { + public int expires; + + public static UserStatus TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + UserStatus result = null; + switch(constructor) { + case 0x8c703f: + result = new TL_userStatusOffline(); + break; + case 0x7bf09fc: + result = new TL_userStatusLastWeek(); + break; + case 0x9d05049: + result = new TL_userStatusEmpty(); + break; + case 0x77ebc742: + result = new TL_userStatusLastMonth(); + break; + case 0xedb93949: + result = new TL_userStatusOnline(); + break; + case 0xe26f42f1: + result = new TL_userStatusRecently(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in UserStatus", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_userStatusOffline extends UserStatus { + public static int constructor = 0x8c703f; + + + public void readParams(AbsSerializedData stream, boolean exception) { + expires = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(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; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + 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; + + + public void readParams(AbsSerializedData stream, boolean exception) { + expires = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(expires); + } + } + + public static class TL_userStatusRecently extends UserStatus { + public static int constructor = 0xe26f42f1; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_userFull extends TLObject { + public static int constructor = 0x771095da; + + public User user; + public TL_contacts_link link; + public Photo profile_photo; + public PeerNotifySettings notify_settings; + public boolean blocked; + public String real_first_name; + public String real_last_name; + + public static TL_userFull TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_userFull.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_userFull", constructor)); + } else { + return null; + } + } + TL_userFull result = new TL_userFull(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + user = User.TLdeserialize(stream, stream.readInt32(exception), exception); + link = TL_contacts_link.TLdeserialize(stream, stream.readInt32(exception), exception); + profile_photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + notify_settings = PeerNotifySettings.TLdeserialize(stream, stream.readInt32(exception), exception); + blocked = stream.readBool(exception); + real_first_name = stream.readString(exception); + real_last_name = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + user.serializeToStream(stream); + link.serializeToStream(stream); + profile_photo.serializeToStream(stream); + notify_settings.serializeToStream(stream); + stream.writeBool(blocked); + stream.writeString(real_first_name); + stream.writeString(real_last_name); + } + } + + public static class TL_msg_resend_req extends TLObject { + public static int constructor = 0x7d861a08; + + public ArrayList msg_ids = new ArrayList<>(); + + public static TL_msg_resend_req TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_msg_resend_req.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_msg_resend_req", constructor)); + } else { + return null; + } + } + TL_msg_resend_req result = new TL_msg_resend_req(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + msg_ids.add(stream.readInt64(exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = msg_ids.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt64(msg_ids.get(a)); + } + } + } + + public static class TL_contact extends TLObject { + public static int constructor = 0xf911c994; + + public int user_id; + public boolean mutual; + + public static TL_contact TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_contact.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_contact", constructor)); + } else { + return null; + } + } + TL_contact result = new TL_contact(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + mutual = stream.readBool(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + stream.writeBool(mutual); + } + } + + public static class TL_chatLocated extends TLObject { + public static int constructor = 0x3631cf4c; + + public int chat_id; + public int distance; + + public static TL_chatLocated TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_chatLocated.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_chatLocated", constructor)); + } else { + return null; + } + } + TL_chatLocated result = new TL_chatLocated(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + distance = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + stream.writeInt32(distance); + } + } + + public static class RpcError extends TLObject { + public int error_code; + public String error_message; + public long query_id; + + public static RpcError TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + RpcError result = null; + switch(constructor) { + case 0x2144ca19: + result = new TL_rpc_error(); + break; + case 0x7ae432f5: + result = new TL_rpc_req_error(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in RpcError", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_rpc_error extends RpcError { + public static int constructor = 0x2144ca19; + + + public void readParams(AbsSerializedData stream, boolean exception) { + error_code = stream.readInt32(exception); + error_message = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(error_code); + stream.writeString(error_message); + } + } + + public static class TL_rpc_req_error extends RpcError { + public static int constructor = 0x7ae432f5; + + + public void readParams(AbsSerializedData stream, boolean exception) { + query_id = stream.readInt64(exception); + error_code = stream.readInt32(exception); + error_message = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(query_id); + stream.writeInt32(error_code); + stream.writeString(error_message); + } + } + + public static class TL_privacyKeyStatusTimestamp extends TLObject { + public static int constructor = 0xbc2eab30; + + + public static TL_privacyKeyStatusTimestamp TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_privacyKeyStatusTimestamp.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_privacyKeyStatusTimestamp", constructor)); + } else { + return null; + } + } + TL_privacyKeyStatusTimestamp result = new TL_privacyKeyStatusTimestamp(); + result.readParams(stream, exception); + return result; + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class ChatParticipants extends TLObject { + public int chat_id; + public int admin_id; + public ArrayList participants = new ArrayList<>(); + public int version; + + public static ChatParticipants TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + ChatParticipants result = null; + switch(constructor) { + case 0x7841b415: + result = new TL_chatParticipants(); + break; + case 0xfd2bb8a: + result = new TL_chatParticipantsForbidden(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in ChatParticipants", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_chatParticipants extends ChatParticipants { + public static int constructor = 0x7841b415; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + admin_id = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_chatParticipant object = TL_chatParticipant.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + participants.add(object); + } + version = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + stream.writeInt32(admin_id); + stream.writeInt32(0x1cb5c415); + int count = participants.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + participants.get(a).serializeToStream(stream); + } + stream.writeInt32(version); + } + } + + public static class TL_chatParticipantsForbidden extends ChatParticipants { + public static int constructor = 0xfd2bb8a; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + } + } + + public static class TL_auth_passwordRecovery extends TLObject { + public static int constructor = 0x137948a5; + + public String email_pattern; + + public static TL_auth_passwordRecovery TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_auth_passwordRecovery.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_auth_passwordRecovery", constructor)); + } else { + return null; + } + } + TL_auth_passwordRecovery result = new TL_auth_passwordRecovery(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + email_pattern = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(email_pattern); + } + } + + public static class DecryptedMessage extends TLObject { + public long random_id; + public DecryptedMessageAction action; + public byte[] random_bytes; + public String message; + public DecryptedMessageMedia media; + public int ttl; + + public static DecryptedMessage TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + DecryptedMessage result = null; + switch(constructor) { + case 0x73164160: + result = new TL_decryptedMessageService(); + break; + case 0x1f814f1f: + result = new TL_decryptedMessage_old(); + break; + case 0x204d3878: + result = new TL_decryptedMessage(); + break; + case 0xaa48327d: + result = new TL_decryptedMessageService_old(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in DecryptedMessage", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_decryptedMessageService extends DecryptedMessage { + public static int constructor = 0x73164160; + + + public void readParams(AbsSerializedData stream, boolean exception) { + random_id = stream.readInt64(exception); + action = DecryptedMessageAction.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(random_id); + action.serializeToStream(stream); + } + } + + public static class TL_decryptedMessage_old extends TL_decryptedMessage { + public static int constructor = 0x1f814f1f; + + + public void readParams(AbsSerializedData stream, boolean exception) { + random_id = stream.readInt64(exception); + random_bytes = stream.readByteArray(exception); + message = stream.readString(exception); + media = DecryptedMessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(random_id); + stream.writeByteArray(random_bytes); + stream.writeString(message); + media.serializeToStream(stream); + } + } + + public static class TL_decryptedMessage extends DecryptedMessage { + public static int constructor = 0x204d3878; + + + public void readParams(AbsSerializedData stream, boolean exception) { + random_id = stream.readInt64(exception); + ttl = stream.readInt32(exception); + message = stream.readString(exception); + media = DecryptedMessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(random_id); + stream.writeInt32(ttl); + stream.writeString(message); + media.serializeToStream(stream); + } + } + + public static class TL_decryptedMessageService_old extends TL_decryptedMessageService { + public static int constructor = 0xaa48327d; + + + public void readParams(AbsSerializedData stream, boolean exception) { + random_id = stream.readInt64(exception); + random_bytes = stream.readByteArray(exception); + action = DecryptedMessageAction.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(random_id); + stream.writeByteArray(random_bytes); + action.serializeToStream(stream); + } + } + + public static class messages_Messages extends TLObject { + public ArrayList messages = new ArrayList<>(); + public ArrayList chats = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + public int count; + + public static messages_Messages TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + messages_Messages result = null; + switch(constructor) { + case 0x8c718e87: + result = new TL_messages_messages(); + break; + case 0xb446ae3: + result = new TL_messages_messagesSlice(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in messages_Messages", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_messages_messages extends messages_Messages { + public static int constructor = 0x8c718e87; + + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Message object = Message.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + messages.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Chat object = Chat.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + chats.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = messages.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + messages.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.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 TL_messages_messagesSlice extends messages_Messages { + public static int constructor = 0xb446ae3; + + + public void readParams(AbsSerializedData stream, boolean exception) { + count = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Message object = Message.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + messages.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Chat object = Chat.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + chats.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(count); + stream.writeInt32(0x1cb5c415); + int count = messages.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + messages.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.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 BadMsgNotification extends TLObject { + public long bad_msg_id; + public int bad_msg_seqno; + public int error_code; + public long new_server_salt; + + public static BadMsgNotification TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + BadMsgNotification result = null; + switch(constructor) { + case 0xa7eff811: + result = new TL_bad_msg_notification(); + break; + case 0xedab447b: + result = new TL_bad_server_salt(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in BadMsgNotification", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_bad_msg_notification extends BadMsgNotification { + public static int constructor = 0xa7eff811; + + + public void readParams(AbsSerializedData stream, boolean exception) { + bad_msg_id = stream.readInt64(exception); + bad_msg_seqno = stream.readInt32(exception); + error_code = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(bad_msg_id); + stream.writeInt32(bad_msg_seqno); + stream.writeInt32(error_code); + } + } + + public static class TL_bad_server_salt extends BadMsgNotification { + public static int constructor = 0xedab447b; + + + public void readParams(AbsSerializedData stream, boolean exception) { + bad_msg_id = stream.readInt64(exception); + bad_msg_seqno = stream.readInt32(exception); + error_code = stream.readInt32(exception); + new_server_salt = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(bad_msg_id); + stream.writeInt32(bad_msg_seqno); + stream.writeInt32(error_code); + stream.writeInt64(new_server_salt); + } + } + + public static class DecryptedMessageMedia extends TLObject { + public int thumb_w; + public int thumb_h; + public String file_name; + public String mime_type; + public int size; + public byte[] key; + public byte[] iv; + public long id; + public long access_hash; + public int date; + public int dc_id; + public ArrayList attributes = new ArrayList<>(); + public int duration; + public double lat; + public double _long; + public int w; + public int h; public String phone_number; public String first_name; public String last_name; - public InputFile file; - public InputFile thumb; - public String mime_type; - public ArrayList attributes = new ArrayList<>(); - public InputGeoPoint geo_point; - public int duration; - public int w; - public int h; + public int user_id; + + public static DecryptedMessageMedia TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + DecryptedMessageMedia result = null; + switch(constructor) { + case 0x89f5c4a: + result = new TL_decryptedMessageMediaEmpty(); + break; + case 0xb095434b: + result = new TL_decryptedMessageMediaDocument(); + break; + case 0xfa95b0dd: + result = new TL_decryptedMessageMediaExternalDocument(); + break; + case 0x6080758f: + result = new TL_decryptedMessageMediaAudio_old(); + break; + case 0x35480a59: + result = new TL_decryptedMessageMediaGeoPoint(); + break; + case 0x57e0a9cb: + result = new TL_decryptedMessageMediaAudio(); + break; + case 0x524a415d: + result = new TL_decryptedMessageMediaVideo(); + break; + case 0x588a0a97: + result = new TL_decryptedMessageMediaContact(); + break; + case 0x32798a8c: + result = new TL_decryptedMessageMediaPhoto(); + break; + case 0x4cee6ef3: + result = new TL_decryptedMessageMediaVideo_old(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in DecryptedMessageMedia", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } } - public static class TL_inputMediaContact extends InputMedia { - public static int constructor = 0xa6e45987; + public static class TL_decryptedMessageMediaEmpty extends DecryptedMessageMedia { + public static int constructor = 0x89f5c4a; - public void readParams(AbsSerializedData stream) { - phone_number = stream.readString(); - first_name = stream.readString(); - last_name = stream.readString(); + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_decryptedMessageMediaDocument extends DecryptedMessageMedia { + public static int constructor = 0xb095434b; + + public byte[] thumb; + + public void readParams(AbsSerializedData stream, boolean exception) { + thumb = stream.readByteArray(exception); + thumb_w = stream.readInt32(exception); + thumb_h = stream.readInt32(exception); + file_name = stream.readString(exception); + mime_type = stream.readString(exception); + size = stream.readInt32(exception); + key = stream.readByteArray(exception); + iv = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeByteArray(thumb); + stream.writeInt32(thumb_w); + stream.writeInt32(thumb_h); + stream.writeString(file_name); + stream.writeString(mime_type); + stream.writeInt32(size); + stream.writeByteArray(key); + stream.writeByteArray(iv); + } + } + + public static class TL_decryptedMessageMediaExternalDocument extends DecryptedMessageMedia { + public static int constructor = 0xfa95b0dd; + + public PhotoSize thumb; + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + date = stream.readInt32(exception); + mime_type = stream.readString(exception); + size = stream.readInt32(exception); + thumb = PhotoSize.TLdeserialize(stream, stream.readInt32(exception), exception); + dc_id = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + DocumentAttribute object = DocumentAttribute.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + attributes.add(object); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(date); + stream.writeString(mime_type); + stream.writeInt32(size); + thumb.serializeToStream(stream); + stream.writeInt32(dc_id); + stream.writeInt32(0x1cb5c415); + int count = attributes.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + attributes.get(a).serializeToStream(stream); + } + } + } + + public static class TL_decryptedMessageMediaAudio_old extends TL_decryptedMessageMediaAudio { + public static int constructor = 0x6080758f; + + + public void readParams(AbsSerializedData stream, boolean exception) { + duration = stream.readInt32(exception); + size = stream.readInt32(exception); + key = stream.readByteArray(exception); + iv = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(duration); + stream.writeInt32(size); + stream.writeByteArray(key); + stream.writeByteArray(iv); + } + } + + public static class TL_decryptedMessageMediaGeoPoint extends DecryptedMessageMedia { + public static int constructor = 0x35480a59; + + + public void readParams(AbsSerializedData stream, boolean exception) { + lat = stream.readDouble(exception); + _long = stream.readDouble(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeDouble(lat); + stream.writeDouble(_long); + } + } + + public static class TL_decryptedMessageMediaAudio extends DecryptedMessageMedia { + public static int constructor = 0x57e0a9cb; + + + public void readParams(AbsSerializedData stream, boolean exception) { + duration = stream.readInt32(exception); + mime_type = stream.readString(exception); + size = stream.readInt32(exception); + key = stream.readByteArray(exception); + iv = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(duration); + stream.writeString(mime_type); + stream.writeInt32(size); + stream.writeByteArray(key); + stream.writeByteArray(iv); + } + } + + public static class TL_decryptedMessageMediaVideo extends DecryptedMessageMedia { + public static int constructor = 0x524a415d; + + public byte[] thumb; + + public void readParams(AbsSerializedData stream, boolean exception) { + thumb = stream.readByteArray(exception); + thumb_w = stream.readInt32(exception); + thumb_h = stream.readInt32(exception); + duration = stream.readInt32(exception); + mime_type = stream.readString(exception); + w = stream.readInt32(exception); + h = stream.readInt32(exception); + size = stream.readInt32(exception); + key = stream.readByteArray(exception); + iv = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeByteArray(thumb); + stream.writeInt32(thumb_w); + stream.writeInt32(thumb_h); + stream.writeInt32(duration); + stream.writeString(mime_type); + stream.writeInt32(w); + stream.writeInt32(h); + stream.writeInt32(size); + stream.writeByteArray(key); + stream.writeByteArray(iv); + } + } + + public static class TL_decryptedMessageMediaContact extends DecryptedMessageMedia { + public static int constructor = 0x588a0a97; + + + public void readParams(AbsSerializedData stream, boolean exception) { + phone_number = stream.readString(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + user_id = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { @@ -3315,223 +2912,6335 @@ public class TLRPC { stream.writeString(phone_number); stream.writeString(first_name); stream.writeString(last_name); + stream.writeInt32(user_id); } } - public static class TL_inputMediaUploadedThumbDocument extends InputMedia { - public static int constructor = 0x41481486; + public static class TL_decryptedMessageMediaPhoto extends DecryptedMessageMedia { + public static int constructor = 0x32798a8c; + public byte[] thumb; - public void readParams(AbsSerializedData stream) { - file = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - thumb = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - mime_type = stream.readString(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - attributes.add((DocumentAttribute)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } + public void readParams(AbsSerializedData stream, boolean exception) { + thumb = stream.readByteArray(exception); + thumb_w = stream.readInt32(exception); + thumb_h = stream.readInt32(exception); + w = stream.readInt32(exception); + h = stream.readInt32(exception); + size = stream.readInt32(exception); + key = stream.readByteArray(exception); + iv = stream.readByteArray(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - file.serializeToStream(stream); - thumb.serializeToStream(stream); - stream.writeString(mime_type); - stream.writeInt32(0x1cb5c415); - int count = attributes.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - attributes.get(a).serializeToStream(stream); - } + stream.writeByteArray(thumb); + stream.writeInt32(thumb_w); + stream.writeInt32(thumb_h); + stream.writeInt32(w); + stream.writeInt32(h); + stream.writeInt32(size); + stream.writeByteArray(key); + stream.writeByteArray(iv); } } - public static class TL_inputMediaAudio extends InputMedia { - public static int constructor = 0x89938781; + public static class TL_decryptedMessageMediaVideo_old extends TL_decryptedMessageMediaVideo { + public static int constructor = 0x4cee6ef3; - public InputAudio id; + public byte[] thumb; - public void readParams(AbsSerializedData stream) { - id = (InputAudio)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public void readParams(AbsSerializedData stream, boolean exception) { + thumb = stream.readByteArray(exception); + thumb_w = stream.readInt32(exception); + thumb_h = stream.readInt32(exception); + duration = stream.readInt32(exception); + w = stream.readInt32(exception); + h = stream.readInt32(exception); + size = stream.readInt32(exception); + key = stream.readByteArray(exception); + iv = stream.readByteArray(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - id.serializeToStream(stream); - } - } - - public static class TL_inputMediaDocument extends InputMedia { - public static int constructor = 0xd184e841; - - public InputDocument id; - - public void readParams(AbsSerializedData stream) { - id = (InputDocument)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - id.serializeToStream(stream); - } - } - - public static class TL_inputMediaVideo extends InputMedia { - public static int constructor = 0x7f023ae6; - - public InputVideo id; - - public void readParams(AbsSerializedData stream) { - id = (InputVideo)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - id.serializeToStream(stream); - } - } - - public static class TL_inputMediaGeoPoint extends InputMedia { - public static int constructor = 0xf9c44144; - - - public void readParams(AbsSerializedData stream) { - geo_point = (InputGeoPoint)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - geo_point.serializeToStream(stream); - } - } - - public static class TL_inputMediaEmpty extends InputMedia { - public static int constructor = 0x9664f57f; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputMediaUploadedThumbVideo extends InputMedia { - public static int constructor = 0x9912dabf; - - - public void readParams(AbsSerializedData stream) { - file = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - thumb = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - duration = stream.readInt32(); - w = stream.readInt32(); - h = stream.readInt32(); - mime_type = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - file.serializeToStream(stream); - thumb.serializeToStream(stream); + stream.writeByteArray(thumb); + stream.writeInt32(thumb_w); + stream.writeInt32(thumb_h); stream.writeInt32(duration); stream.writeInt32(w); stream.writeInt32(h); - stream.writeString(mime_type); + stream.writeInt32(size); + stream.writeByteArray(key); + stream.writeByteArray(iv); } } - public static class TL_inputMediaUploadedPhoto extends InputMedia { - public static int constructor = 0x2dc53a7d; + public static class User extends TLObject { + public int id; + public String first_name; + public String last_name; + public long access_hash; + public String phone; + public UserProfilePhoto photo; + public UserStatus status; + public boolean inactive; + public String username; + + public static User TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + User result = null; + switch(constructor) { + case 0xf2fb8319: + result = new TL_userContact_old(); + break; + case 0x720535ec: + result = new TL_userSelf_old(); + break; + case 0xcab35e18: + result = new TL_userContact(); + break; + case 0x1c60e608: + result = new TL_userSelf(); + break; + case 0x75cf7a8: + result = new TL_userForeign(); + break; + case 0x200250ba: + result = new TL_userEmpty(); + break; + case 0x22e8ceb0: + result = new TL_userRequest_old(); + break; + case 0x5214c89d: + result = new TL_userForeign_old(); + break; + case 0xd9ccc4ef: + result = new TL_userRequest(); + break; + case 0x7007b451: + result = new TL_userSelf_old2(); + break; + case 0xb29ad7cc: + result = new TL_userDeleted_old(); + break; + case 0xd6016d7a: + result = new TL_userDeleted(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in User", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_userContact_old extends TL_userContact { + public static int constructor = 0xf2fb8319; - public void readParams(AbsSerializedData stream) { - file = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + access_hash = stream.readInt64(exception); + phone = stream.readString(exception); + photo = UserProfilePhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + status = UserStatus.TLdeserialize(stream, stream.readInt32(exception), exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - file.serializeToStream(stream); + stream.writeInt32(id); + stream.writeString(first_name); + stream.writeString(last_name); + stream.writeInt64(access_hash); + stream.writeString(phone); + photo.serializeToStream(stream); + status.serializeToStream(stream); } } - public static class TL_inputMediaUploadedAudio extends InputMedia { - public static int constructor = 0x4e498cab; + public static class TL_userSelf_old extends TL_userSelf { + public static int constructor = 0x720535ec; - public void readParams(AbsSerializedData stream) { - file = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - duration = stream.readInt32(); - mime_type = stream.readString(); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + phone = stream.readString(exception); + photo = UserProfilePhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + status = UserStatus.TLdeserialize(stream, stream.readInt32(exception), exception); + inactive = stream.readBool(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - file.serializeToStream(stream); - stream.writeInt32(duration); - stream.writeString(mime_type); + stream.writeInt32(id); + stream.writeString(first_name); + stream.writeString(last_name); + stream.writeString(phone); + photo.serializeToStream(stream); + status.serializeToStream(stream); + stream.writeBool(inactive); } } - public static class TL_inputMediaUploadedVideo extends InputMedia { - public static int constructor = 0x133ad6f6; + public static class TL_userContact extends User { + public static int constructor = 0xcab35e18; - public void readParams(AbsSerializedData stream) { - file = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - duration = stream.readInt32(); - w = stream.readInt32(); - h = stream.readInt32(); - mime_type = stream.readString(); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + username = stream.readString(exception); + access_hash = stream.readInt64(exception); + phone = stream.readString(exception); + photo = UserProfilePhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + status = UserStatus.TLdeserialize(stream, stream.readInt32(exception), exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - file.serializeToStream(stream); - stream.writeInt32(duration); - stream.writeInt32(w); - stream.writeInt32(h); - stream.writeString(mime_type); + stream.writeInt32(id); + stream.writeString(first_name); + stream.writeString(last_name); + stream.writeString(username); + stream.writeInt64(access_hash); + stream.writeString(phone); + photo.serializeToStream(stream); + status.serializeToStream(stream); } } - public static class TL_inputMediaUploadedDocument extends InputMedia { - public static int constructor = 0xffe76b78; + public static class TL_userSelf extends User { + public static int constructor = 0x1c60e608; - public void readParams(AbsSerializedData stream) { - file = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - mime_type = stream.readString(); - stream.readInt32(); - int count = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + username = stream.readString(exception); + phone = stream.readString(exception); + photo = UserProfilePhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + status = UserStatus.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeString(first_name); + stream.writeString(last_name); + stream.writeString(username); + stream.writeString(phone); + photo.serializeToStream(stream); + status.serializeToStream(stream); + } + } + + public static class TL_userForeign extends User { + public static int constructor = 0x75cf7a8; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + username = stream.readString(exception); + access_hash = stream.readInt64(exception); + photo = UserProfilePhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + status = UserStatus.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeString(first_name); + stream.writeString(last_name); + stream.writeString(username); + stream.writeInt64(access_hash); + photo.serializeToStream(stream); + status.serializeToStream(stream); + } + } + + public static class TL_userRequest_old extends TL_userRequest { + public static int constructor = 0x22e8ceb0; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + access_hash = stream.readInt64(exception); + phone = stream.readString(exception); + photo = UserProfilePhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + status = UserStatus.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeString(first_name); + stream.writeString(last_name); + stream.writeInt64(access_hash); + stream.writeString(phone); + photo.serializeToStream(stream); + status.serializeToStream(stream); + } + } + + public static class TL_userForeign_old extends TL_userForeign { + public static int constructor = 0x5214c89d; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + access_hash = stream.readInt64(exception); + photo = UserProfilePhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + status = UserStatus.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeString(first_name); + stream.writeString(last_name); + stream.writeInt64(access_hash); + photo.serializeToStream(stream); + status.serializeToStream(stream); + } + } + + public static class TL_userRequest extends User { + public static int constructor = 0xd9ccc4ef; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + username = stream.readString(exception); + access_hash = stream.readInt64(exception); + phone = stream.readString(exception); + photo = UserProfilePhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + status = UserStatus.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeString(first_name); + stream.writeString(last_name); + stream.writeString(username); + stream.writeInt64(access_hash); + stream.writeString(phone); + photo.serializeToStream(stream); + status.serializeToStream(stream); + } + } + + public static class TL_userSelf_old2 extends TL_userSelf { + public static int constructor = 0x7007b451; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + username = stream.readString(exception); + phone = stream.readString(exception); + photo = UserProfilePhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + status = UserStatus.TLdeserialize(stream, stream.readInt32(exception), exception); + inactive = stream.readBool(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeString(first_name); + stream.writeString(last_name); + stream.writeString(username); + stream.writeString(phone); + photo.serializeToStream(stream); + status.serializeToStream(stream); + stream.writeBool(inactive); + } + } + + public static class TL_userDeleted_old extends TL_userDeleted { + public static int constructor = 0xb29ad7cc; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeString(first_name); + stream.writeString(last_name); + } + } + + public static class TL_userDeleted extends User { + public static int constructor = 0xd6016d7a; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + username = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeString(first_name); + stream.writeString(last_name); + stream.writeString(username); + } + } + + public static class MessageMedia extends TLObject { + public byte[] bytes; + public Video video; + public String caption; + public Photo photo; + public Audio audio; + public GeoPoint geo; + public String title; + public String address; + public String provider; + public String venue_id; + public Document document; + public String phone_number; + public String first_name; + public String last_name; + public int user_id; + public WebPage webpage; + + public static MessageMedia TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + MessageMedia result = null; + switch(constructor) { + case 0x29632a36: + result = new TL_messageMediaUnsupported_old(); + break; + case 0x5bcf1675: + result = new TL_messageMediaVideo(); + break; + case 0xc8c45a2a: + result = new TL_messageMediaPhoto_old(); + break; + case 0xc6b68300: + result = new TL_messageMediaAudio(); + break; + case 0x9f84f49e: + result = new TL_messageMediaUnsupported(); + break; + case 0x3ded6320: + result = new TL_messageMediaEmpty(); + break; + case 0x7912b71f: + result = new TL_messageMediaVenue(); + break; + case 0xa2d24290: + result = new TL_messageMediaVideo_old(); + break; + case 0x2fda2204: + result = new TL_messageMediaDocument(); + break; + case 0x5e7d2f39: + result = new TL_messageMediaContact(); + break; + case 0x3d8ce53d: + result = new TL_messageMediaPhoto(); + break; + case 0xa32dd600: + result = new TL_messageMediaWebPage(); + break; + case 0x56e0d474: + result = new TL_messageMediaGeo(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in MessageMedia", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_messageMediaUnsupported_old extends TL_messageMediaUnsupported { + public static int constructor = 0x29632a36; + + + public void readParams(AbsSerializedData stream, boolean exception) { + bytes = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeByteArray(bytes); + } + } + + public static class TL_messageMediaVideo extends MessageMedia { + public static int constructor = 0x5bcf1675; + + + public void readParams(AbsSerializedData stream, boolean exception) { + video = Video.TLdeserialize(stream, stream.readInt32(exception), exception); + caption = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + video.serializeToStream(stream); + stream.writeString(caption); + } + } + + public static class TL_messageMediaPhoto_old extends TL_messageMediaPhoto { + public static int constructor = 0xc8c45a2a; + + + public void readParams(AbsSerializedData stream, boolean exception) { + photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + photo.serializeToStream(stream); + } + } + + public static class TL_messageMediaAudio extends MessageMedia { + public static int constructor = 0xc6b68300; + + + public void readParams(AbsSerializedData stream, boolean exception) { + audio = Audio.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + audio.serializeToStream(stream); + } + } + + public static class TL_messageMediaUnsupported extends MessageMedia { + public static int constructor = 0x9f84f49e; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_messageMediaEmpty extends MessageMedia { + public static int constructor = 0x3ded6320; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_messageMediaVenue extends MessageMedia { + public static int constructor = 0x7912b71f; + + + public void readParams(AbsSerializedData stream, boolean exception) { + geo = GeoPoint.TLdeserialize(stream, stream.readInt32(exception), exception); + title = stream.readString(exception); + address = stream.readString(exception); + provider = stream.readString(exception); + venue_id = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + geo.serializeToStream(stream); + stream.writeString(title); + stream.writeString(address); + stream.writeString(provider); + stream.writeString(venue_id); + } + } + + public static class TL_messageMediaVideo_old extends TL_messageMediaVideo { + public static int constructor = 0xa2d24290; + + + public void readParams(AbsSerializedData stream, boolean exception) { + video = Video.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + video.serializeToStream(stream); + } + } + + public static class TL_messageMediaDocument extends MessageMedia { + public static int constructor = 0x2fda2204; + + + public void readParams(AbsSerializedData stream, boolean exception) { + document = Document.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + document.serializeToStream(stream); + } + } + + public static class TL_messageMediaContact extends MessageMedia { + public static int constructor = 0x5e7d2f39; + + + public void readParams(AbsSerializedData stream, boolean exception) { + phone_number = stream.readString(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + user_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(phone_number); + stream.writeString(first_name); + stream.writeString(last_name); + stream.writeInt32(user_id); + } + } + + public static class TL_messageMediaPhoto extends MessageMedia { + public static int constructor = 0x3d8ce53d; + + + public void readParams(AbsSerializedData stream, boolean exception) { + photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + caption = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + photo.serializeToStream(stream); + stream.writeString(caption); + } + } + + public static class TL_messageMediaWebPage extends MessageMedia { + public static int constructor = 0xa32dd600; + + + public void readParams(AbsSerializedData stream, boolean exception) { + webpage = WebPage.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + webpage.serializeToStream(stream); + } + } + + public static class TL_messageMediaGeo extends MessageMedia { + public static int constructor = 0x56e0d474; + + + public void readParams(AbsSerializedData stream, boolean exception) { + geo = GeoPoint.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + geo.serializeToStream(stream); + } + } + + public static class TL_nearestDc extends TLObject { + public static int constructor = 0x8e1a1775; + + public String country; + public int this_dc; + public int nearest_dc; + + public static TL_nearestDc TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_nearestDc.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_nearestDc", constructor)); + } else { + return null; + } + } + TL_nearestDc result = new TL_nearestDc(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + country = stream.readString(exception); + this_dc = stream.readInt32(exception); + nearest_dc = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(country); + stream.writeInt32(this_dc); + stream.writeInt32(nearest_dc); + } + } + + public static class TL_contactFound extends TLObject { + public static int constructor = 0xea879f95; + + public int user_id; + + public static TL_contactFound TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_contactFound.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_contactFound", constructor)); + } else { + return null; + } + } + TL_contactFound result = new TL_contactFound(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + } + } + + public static class TL_new_session_created extends TLObject { + public static int constructor = 0x9ec20908; + + public long first_msg_id; + public long unique_id; + public long server_salt; + + public static TL_new_session_created TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_new_session_created.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_new_session_created", constructor)); + } else { + return null; + } + } + TL_new_session_created result = new TL_new_session_created(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + first_msg_id = stream.readInt64(exception); + unique_id = stream.readInt64(exception); + server_salt = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(first_msg_id); + stream.writeInt64(unique_id); + stream.writeInt64(server_salt); + } + } + + public static class TL_contacts_suggested extends TLObject { + public static int constructor = 0x5649dcc5; + + public ArrayList results = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + + public static TL_contacts_suggested TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_contacts_suggested.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_contacts_suggested", constructor)); + } else { + return null; + } + } + TL_contacts_suggested result = new TL_contacts_suggested(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - attributes.add((DocumentAttribute)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + TL_contactSuggested object = TL_contactSuggested.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + results.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); } } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - file.serializeToStream(stream); - stream.writeString(mime_type); stream.writeInt32(0x1cb5c415); - int count = attributes.size(); + int count = results.size(); stream.writeInt32(count); for (int a = 0; a < count; a++) { - attributes.get(a).serializeToStream(stream); + results.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 TL_inputMediaPhoto extends InputMedia { - public static int constructor = 0x8f2ab2ec; + public static class WallPaper extends TLObject { + public int id; + public String title; + public ArrayList sizes = new ArrayList<>(); + public int color; + public int bg_color; + public static WallPaper TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + WallPaper result = null; + switch(constructor) { + case 0xccb03657: + result = new TL_wallPaper(); + break; + case 0x63117f24: + result = new TL_wallPaperSolid(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in WallPaper", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_wallPaper extends WallPaper { + public static int constructor = 0xccb03657; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + title = stream.readString(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + PhotoSize object = PhotoSize.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + sizes.add(object); + } + color = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeString(title); + stream.writeInt32(0x1cb5c415); + int count = sizes.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + sizes.get(a).serializeToStream(stream); + } + stream.writeInt32(color); + } + } + + public static class TL_wallPaperSolid extends WallPaper { + public static int constructor = 0x63117f24; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + title = stream.readString(exception); + bg_color = stream.readInt32(exception); + color = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeString(title); + stream.writeInt32(bg_color); + stream.writeInt32(color); + } + } + + public static class NotifyPeer extends TLObject { + public Peer peer; + + public static NotifyPeer TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + NotifyPeer result = null; + switch(constructor) { + case 0x74d07c60: + result = new TL_notifyAll(); + break; + case 0xc007cec3: + result = new TL_notifyChats(); + break; + case 0xb4c83b4c: + result = new TL_notifyUsers(); + break; + case 0x9fd40bd8: + result = new TL_notifyPeer(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in NotifyPeer", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_notifyAll extends NotifyPeer { + public static int constructor = 0x74d07c60; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_notifyChats extends NotifyPeer { + public static int constructor = 0xc007cec3; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_notifyUsers extends NotifyPeer { + public static int constructor = 0xb4c83b4c; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_notifyPeer extends NotifyPeer { + public static int constructor = 0x9fd40bd8; + + + public void readParams(AbsSerializedData stream, boolean exception) { + peer = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + peer.serializeToStream(stream); + } + } + + public static class TL_inputPrivacyKeyStatusTimestamp extends TLObject { + public static int constructor = 0x4f96cb18; + + + public static TL_inputPrivacyKeyStatusTimestamp TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_inputPrivacyKeyStatusTimestamp.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_inputPrivacyKeyStatusTimestamp", constructor)); + } else { + return null; + } + } + TL_inputPrivacyKeyStatusTimestamp result = new TL_inputPrivacyKeyStatusTimestamp(); + result.readParams(stream, exception); + return result; + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_account_sentChangePhoneCode extends TLObject { + public static int constructor = 0xa4f58c4c; + + public String phone_code_hash; + public int send_call_timeout; + + public static TL_account_sentChangePhoneCode TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_account_sentChangePhoneCode.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_account_sentChangePhoneCode", constructor)); + } else { + return null; + } + } + TL_account_sentChangePhoneCode result = new TL_account_sentChangePhoneCode(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + phone_code_hash = stream.readString(exception); + send_call_timeout = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(phone_code_hash); + stream.writeInt32(send_call_timeout); + } + } + + public static class ChatInvite extends TLObject { + public String title; + public Chat chat; + + public static ChatInvite TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + ChatInvite result = null; + switch(constructor) { + case 0xce917dcd: + result = new TL_chatInvite(); + break; + case 0x5a686d7c: + result = new TL_chatInviteAlready(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in ChatInvite", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_chatInvite extends ChatInvite { + public static int constructor = 0xce917dcd; + + + public void readParams(AbsSerializedData stream, boolean exception) { + title = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(title); + } + } + + public static class TL_chatInviteAlready extends ChatInvite { + public static int constructor = 0x5a686d7c; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat = Chat.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + chat.serializeToStream(stream); + } + } + + public static class TL_photos_photo extends TLObject { + public static int constructor = 0x20212ca8; + + public Photo photo; + public ArrayList users = new ArrayList<>(); + + public static TL_photos_photo TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_photos_photo.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_photos_photo", constructor)); + } else { + return null; + } + } + TL_photos_photo result = new TL_photos_photo(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + photo.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + } + } + + public static class TL_resPQ extends TLObject { + public static int constructor = 0x05162463; + + public byte[] nonce; + public byte[] server_nonce; + public byte[] pq; + public ArrayList server_public_key_fingerprints = new ArrayList<>(); + + public static TL_resPQ TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_resPQ.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_resPQ", constructor)); + } else { + return null; + } + } + TL_resPQ result = new TL_resPQ(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + nonce = stream.readData(16, exception); + server_nonce = stream.readData(16, exception); + pq = stream.readByteArray(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + server_public_key_fingerprints.add(stream.readInt64(exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeRaw(nonce); + stream.writeRaw(server_nonce); + stream.writeByteArray(pq); + stream.writeInt32(0x1cb5c415); + int count = server_public_key_fingerprints.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt64(server_public_key_fingerprints.get(a)); + } + } + } + + public static class messages_SentEncryptedMessage extends TLObject { + public int date; + public EncryptedFile file; + + public static messages_SentEncryptedMessage TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + messages_SentEncryptedMessage result = null; + switch(constructor) { + case 0x560f8935: + result = new TL_messages_sentEncryptedMessage(); + break; + case 0x9493ff32: + result = new TL_messages_sentEncryptedFile(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in messages_SentEncryptedMessage", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_messages_sentEncryptedMessage extends messages_SentEncryptedMessage { + public static int constructor = 0x560f8935; + + + public void readParams(AbsSerializedData stream, boolean exception) { + date = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(date); + } + } + + public static class TL_messages_sentEncryptedFile extends messages_SentEncryptedMessage { + public static int constructor = 0x9493ff32; + + + public void readParams(AbsSerializedData stream, boolean exception) { + date = stream.readInt32(exception); + file = EncryptedFile.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(date); + file.serializeToStream(stream); + } + } + + public static class Update extends TLObject { + public int chat_id; + public int max_date; + public int date; + public int user_id; + public ContactLink my_link; + public ContactLink foreign_link; + public int max_id; + public int pts; + public int pts_count; + public int version; + public WebPage webpage; + public String type; + public MessageMedia media; + public boolean popup; + public PeerNotifySettings notify_settings; + public SendMessageAction action; + public String first_name; + public String last_name; + public String username; + public int qts; + public int id; + public long random_id; + public ArrayList dc_options = new ArrayList<>(); + public ArrayList messages = new ArrayList<>(); + public ChatParticipants participants; + public TL_privacyKeyStatusTimestamp key; + public ArrayList rules = new ArrayList<>(); + public EncryptedChat chat; + public boolean blocked; + public String phone; + public long auth_key_id; + public String device; + public String location; + public UserProfilePhoto photo; + public boolean previous; + public int inviter_id; + public UserStatus status; + + public static Update TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + Update result = null; + switch(constructor) { + case 0x38fe25b7: + result = new TL_updateEncryptedMessagesRead(); + break; + case 0x9d2e67c5: + result = new TL_updateContactLink(); + break; + case 0x9961fd5c: + result = new TL_updateReadHistoryInbox(); + break; + case 0x2f2f21bf: + result = new TL_updateReadHistoryOutbox(); + break; + case 0x6e5f8c22: + result = new TL_updateChatParticipantDelete(); + break; + case 0x2cc36971: + result = new TL_updateWebPage(); + break; + case 0x382dd3e4: + result = new TL_updateServiceNotification(); + break; + case 0xbec268ef: + result = new TL_updateNotifySettings(); + break; + case 0x5c486927: + result = new TL_updateUserTyping(); + break; + case 0x9a65ea1f: + result = new TL_updateChatUserTyping(); + break; + case 0xa7332b73: + result = new TL_updateUserName(); + break; + case 0x12bcbd9a: + result = new TL_updateNewEncryptedMessage(); + break; + case 0x1f2b0afd: + result = new TL_updateNewMessage(); + break; + case 0x4e90bfd6: + result = new TL_updateMessageID(); + break; + case 0x8e5e9873: + result = new TL_updateDcOptions(); + break; + case 0x1710f156: + result = new TL_updateEncryptedChatTyping(); + break; + case 0xa20db0e5: + result = new TL_updateDeleteMessages(); + break; + case 0x68c13933: + result = new TL_updateReadMessagesContents(); + break; + case 0x7761198: + result = new TL_updateChatParticipants(); + break; + case 0xee3b272a: + result = new TL_updatePrivacy(); + break; + case 0xb4a2e88d: + result = new TL_updateEncryption(); + break; + case 0x80ece81a: + result = new TL_updateUserBlocked(); + break; + case 0x12b9417b: + result = new TL_updateUserPhone(); + break; + case 0x8f06529a: + result = new TL_updateNewAuthorization(); + break; + case 0x5a68e3f7: + result = new TL_updateNewGeoChatMessage(); + break; + case 0x95313b0c: + result = new TL_updateUserPhoto(); + break; + case 0x2575bbb9: + result = new TL_updateContactRegistered(); + break; + case 0x3a0eeb22: + result = new TL_updateChatParticipantAdd(); + break; + case 0x1bfbd823: + result = new TL_updateUserStatus(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in Update", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_updateEncryptedMessagesRead extends Update { + public static int constructor = 0x38fe25b7; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + max_date = stream.readInt32(exception); + date = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + stream.writeInt32(max_date); + stream.writeInt32(date); + } + } + + public static class TL_updateContactLink extends Update { + public static int constructor = 0x9d2e67c5; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + my_link = ContactLink.TLdeserialize(stream, stream.readInt32(exception), exception); + foreign_link = ContactLink.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + my_link.serializeToStream(stream); + foreign_link.serializeToStream(stream); + } + } + + public static class TL_updateReadHistoryInbox extends Update { + public static int constructor = 0x9961fd5c; + + public Peer peer; + + public void readParams(AbsSerializedData stream, boolean exception) { + peer = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + max_id = stream.readInt32(exception); + pts = stream.readInt32(exception); + pts_count = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + peer.serializeToStream(stream); + stream.writeInt32(max_id); + stream.writeInt32(pts); + stream.writeInt32(pts_count); + } + } + + public static class TL_updateReadHistoryOutbox extends Update { + public static int constructor = 0x2f2f21bf; + + public Peer peer; + + public void readParams(AbsSerializedData stream, boolean exception) { + peer = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + max_id = stream.readInt32(exception); + pts = stream.readInt32(exception); + pts_count = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + peer.serializeToStream(stream); + stream.writeInt32(max_id); + stream.writeInt32(pts); + stream.writeInt32(pts_count); + } + } + + public static class TL_updateChatParticipantDelete extends Update { + public static int constructor = 0x6e5f8c22; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + user_id = stream.readInt32(exception); + version = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + stream.writeInt32(user_id); + stream.writeInt32(version); + } + } + + public static class TL_updateWebPage extends Update { + public static int constructor = 0x2cc36971; + + + public void readParams(AbsSerializedData stream, boolean exception) { + webpage = WebPage.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + webpage.serializeToStream(stream); + } + } + + public static class TL_updateServiceNotification extends Update { + public static int constructor = 0x382dd3e4; + + public String message; + + public void readParams(AbsSerializedData stream, boolean exception) { + type = stream.readString(exception); + message = stream.readString(exception); + media = MessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); + popup = stream.readBool(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(type); + stream.writeString(message); + media.serializeToStream(stream); + stream.writeBool(popup); + } + } + + public static class TL_updateNotifySettings extends Update { + public static int constructor = 0xbec268ef; + + public NotifyPeer peer; + + public void readParams(AbsSerializedData stream, boolean exception) { + peer = NotifyPeer.TLdeserialize(stream, stream.readInt32(exception), exception); + notify_settings = PeerNotifySettings.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + peer.serializeToStream(stream); + notify_settings.serializeToStream(stream); + } + } + + public static class TL_updateUserTyping extends Update { + public static int constructor = 0x5c486927; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + action = SendMessageAction.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + action.serializeToStream(stream); + } + } + + public static class TL_updateChatUserTyping extends Update { + public static int constructor = 0x9a65ea1f; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + user_id = stream.readInt32(exception); + action = SendMessageAction.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + stream.writeInt32(user_id); + action.serializeToStream(stream); + } + } + + public static class TL_updateUserName extends Update { + public static int constructor = 0xa7332b73; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + username = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + stream.writeString(first_name); + stream.writeString(last_name); + stream.writeString(username); + } + } + + public static class TL_updateNewEncryptedMessage extends Update { + public static int constructor = 0x12bcbd9a; + + public EncryptedMessage message; + + public void readParams(AbsSerializedData stream, boolean exception) { + message = EncryptedMessage.TLdeserialize(stream, stream.readInt32(exception), exception); + qts = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + message.serializeToStream(stream); + stream.writeInt32(qts); + } + } + + public static class TL_updateNewMessage extends Update { + public static int constructor = 0x1f2b0afd; + + public Message message; + + public void readParams(AbsSerializedData stream, boolean exception) { + message = Message.TLdeserialize(stream, stream.readInt32(exception), exception); + pts = stream.readInt32(exception); + pts_count = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + message.serializeToStream(stream); + stream.writeInt32(pts); + stream.writeInt32(pts_count); + } + } + + public static class TL_updateMessageID extends Update { + public static int constructor = 0x4e90bfd6; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + random_id = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeInt64(random_id); + } + } + + public static class TL_updateDcOptions extends Update { + public static int constructor = 0x8e5e9873; + + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_dcOption object = TL_dcOption.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + dc_options.add(object); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = dc_options.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + dc_options.get(a).serializeToStream(stream); + } + } + } + + public static class TL_updateEncryptedChatTyping extends Update { + public static int constructor = 0x1710f156; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + } + } + + public static class TL_updateDeleteMessages extends Update { + public static int constructor = 0xa20db0e5; + + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + messages.add(stream.readInt32(exception)); + } + pts = stream.readInt32(exception); + pts_count = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = messages.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt32(messages.get(a)); + } + stream.writeInt32(pts); + stream.writeInt32(pts_count); + } + } + + public static class TL_updateReadMessagesContents extends Update { + public static int constructor = 0x68c13933; + + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + messages.add(stream.readInt32(exception)); + } + pts = stream.readInt32(exception); + pts_count = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = messages.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt32(messages.get(a)); + } + stream.writeInt32(pts); + stream.writeInt32(pts_count); + } + } + + public static class TL_updateChatParticipants extends Update { + public static int constructor = 0x7761198; + + + public void readParams(AbsSerializedData stream, boolean exception) { + participants = ChatParticipants.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + participants.serializeToStream(stream); + } + } + + public static class TL_updatePrivacy extends Update { + public static int constructor = 0xee3b272a; + + + public void readParams(AbsSerializedData stream, boolean exception) { + key = TL_privacyKeyStatusTimestamp.TLdeserialize(stream, stream.readInt32(exception), exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + PrivacyRule object = PrivacyRule.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + rules.add(object); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + key.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = rules.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + rules.get(a).serializeToStream(stream); + } + } + } + + public static class TL_updateEncryption extends Update { + public static int constructor = 0xb4a2e88d; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat = EncryptedChat.TLdeserialize(stream, stream.readInt32(exception), exception); + date = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + chat.serializeToStream(stream); + stream.writeInt32(date); + } + } + + public static class TL_updateUserBlocked extends Update { + public static int constructor = 0x80ece81a; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + blocked = stream.readBool(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + stream.writeBool(blocked); + } + } + + public static class TL_updateUserPhone extends Update { + public static int constructor = 0x12b9417b; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + phone = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + stream.writeString(phone); + } + } + + public static class TL_updateNewAuthorization extends Update { + public static int constructor = 0x8f06529a; + + + public void readParams(AbsSerializedData stream, boolean exception) { + auth_key_id = stream.readInt64(exception); + date = stream.readInt32(exception); + device = stream.readString(exception); + location = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(auth_key_id); + stream.writeInt32(date); + stream.writeString(device); + stream.writeString(location); + } + } + + public static class TL_updateNewGeoChatMessage extends Update { + public static int constructor = 0x5a68e3f7; + + public GeoChatMessage message; + + public void readParams(AbsSerializedData stream, boolean exception) { + message = GeoChatMessage.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + message.serializeToStream(stream); + } + } + + public static class TL_updateUserPhoto extends Update { + public static int constructor = 0x95313b0c; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); + photo = UserProfilePhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + previous = stream.readBool(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + stream.writeInt32(date); + photo.serializeToStream(stream); + stream.writeBool(previous); + } + } + + public static class TL_updateContactRegistered extends Update { + public static int constructor = 0x2575bbb9; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + stream.writeInt32(date); + } + } + + public static class TL_updateChatParticipantAdd extends Update { + public static int constructor = 0x3a0eeb22; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + user_id = stream.readInt32(exception); + inviter_id = stream.readInt32(exception); + version = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + stream.writeInt32(user_id); + stream.writeInt32(inviter_id); + stream.writeInt32(version); + } + } + + public static class TL_updateUserStatus extends Update { + public static int constructor = 0x1bfbd823; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + status = UserStatus.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + status.serializeToStream(stream); + } + } + + public static class TL_config extends TLObject { + public static int constructor = 0x4e32b894; + + public int date; + public int expires; + public boolean test_mode; + public int this_dc; + public ArrayList dc_options = new ArrayList<>(); + public int chat_size_max; + public int broadcast_size_max; + public int forwarded_count_max; + public int online_update_period_ms; + public int offline_blur_timeout_ms; + public int offline_idle_timeout_ms; + public int online_cloud_timeout_ms; + public int notify_cloud_delay_ms; + public int notify_default_delay_ms; + public int chat_big_size; + public int push_chat_period_ms; + public int push_chat_limit; + public ArrayList disabled_features = new ArrayList<>(); + + public static TL_config TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_config.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_config", constructor)); + } else { + return null; + } + } + TL_config result = new TL_config(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + date = stream.readInt32(exception); + expires = stream.readInt32(exception); + test_mode = stream.readBool(exception); + this_dc = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_dcOption object = TL_dcOption.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + dc_options.add(object); + } + chat_size_max = stream.readInt32(exception); + broadcast_size_max = stream.readInt32(exception); + forwarded_count_max = stream.readInt32(exception); + online_update_period_ms = stream.readInt32(exception); + offline_blur_timeout_ms = stream.readInt32(exception); + offline_idle_timeout_ms = stream.readInt32(exception); + online_cloud_timeout_ms = stream.readInt32(exception); + notify_cloud_delay_ms = stream.readInt32(exception); + notify_default_delay_ms = stream.readInt32(exception); + chat_big_size = stream.readInt32(exception); + push_chat_period_ms = stream.readInt32(exception); + push_chat_limit = stream.readInt32(exception); + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_disabledFeature object = TL_disabledFeature.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + disabled_features.add(object); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(date); + stream.writeInt32(expires); + stream.writeBool(test_mode); + stream.writeInt32(this_dc); + stream.writeInt32(0x1cb5c415); + int count = dc_options.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + dc_options.get(a).serializeToStream(stream); + } + stream.writeInt32(chat_size_max); + stream.writeInt32(broadcast_size_max); + stream.writeInt32(forwarded_count_max); + stream.writeInt32(online_update_period_ms); + stream.writeInt32(offline_blur_timeout_ms); + stream.writeInt32(offline_idle_timeout_ms); + stream.writeInt32(online_cloud_timeout_ms); + stream.writeInt32(notify_cloud_delay_ms); + stream.writeInt32(notify_default_delay_ms); + stream.writeInt32(chat_big_size); + stream.writeInt32(push_chat_period_ms); + stream.writeInt32(push_chat_limit); + stream.writeInt32(0x1cb5c415); + count = disabled_features.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + disabled_features.get(a).serializeToStream(stream); + } + } + } + + public static class InputAudio extends TLObject { + public long id; + public long access_hash; + + public static InputAudio TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputAudio result = null; + switch(constructor) { + case 0x77d440ff: + result = new TL_inputAudio(); + break; + case 0xd95adc84: + result = new TL_inputAudioEmpty(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputAudio", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inputAudio extends InputAudio { + public static int constructor = 0x77d440ff; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + } + } + + public static class TL_inputAudioEmpty extends InputAudio { + public static int constructor = 0xd95adc84; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class PeerNotifySettings extends TLObject { + public int mute_until; + public String sound; + public boolean show_previews; + public int events_mask; + + public static PeerNotifySettings TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + PeerNotifySettings result = null; + switch(constructor) { + case 0x70a68512: + result = new TL_peerNotifySettingsEmpty(); + break; + case 0x8d5e11ee: + result = new TL_peerNotifySettings(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in PeerNotifySettings", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_peerNotifySettingsEmpty extends PeerNotifySettings { + public static int constructor = 0x70a68512; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_peerNotifySettings extends PeerNotifySettings { + public static int constructor = 0x8d5e11ee; + + + public void readParams(AbsSerializedData stream, boolean exception) { + mute_until = stream.readInt32(exception); + sound = stream.readString(exception); + show_previews = stream.readBool(exception); + events_mask = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(mute_until); + stream.writeString(sound); + stream.writeBool(show_previews); + stream.writeInt32(events_mask); + } + } + + public static class GeoChatMessage extends TLObject { + public int chat_id; + public int id; + public int from_id; + public int date; + public String message; + public MessageMedia media; + public MessageAction action; + + public static GeoChatMessage TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + GeoChatMessage result = null; + switch(constructor) { + case 0x4505f8e1: + result = new TL_geoChatMessage(); + break; + case 0xd34fa24e: + result = new TL_geoChatMessageService(); + break; + case 0x60311a9b: + result = new TL_geoChatMessageEmpty(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in GeoChatMessage", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_geoChatMessage extends GeoChatMessage { + public static int constructor = 0x4505f8e1; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + id = stream.readInt32(exception); + from_id = stream.readInt32(exception); + date = stream.readInt32(exception); + message = stream.readString(exception); + media = MessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + stream.writeInt32(id); + stream.writeInt32(from_id); + stream.writeInt32(date); + stream.writeString(message); + media.serializeToStream(stream); + } + } + + public static class TL_geoChatMessageService extends GeoChatMessage { + public static int constructor = 0xd34fa24e; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + id = stream.readInt32(exception); + from_id = stream.readInt32(exception); + date = stream.readInt32(exception); + action = MessageAction.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + stream.writeInt32(id); + stream.writeInt32(from_id); + stream.writeInt32(date); + action.serializeToStream(stream); + } + } + + public static class TL_geoChatMessageEmpty extends GeoChatMessage { + public static int constructor = 0x60311a9b; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + stream.writeInt32(id); + } + } + + public static class messages_SentMessage extends TLObject { + public int id; + public int date; + public MessageMedia media; + public int pts; + public int pts_count; + public ArrayList links = new ArrayList<>(); + public int seq; + + public static messages_SentMessage TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + messages_SentMessage result = null; + switch(constructor) { + case 0x4c3d47f3: + result = new TL_messages_sentMessage(); + break; + case 0x35a1a663: + result = new TL_messages_sentMessageLink(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in messages_SentMessage", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_messages_sentMessage extends messages_SentMessage { + public static int constructor = 0x4c3d47f3; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + date = stream.readInt32(exception); + media = MessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); + pts = stream.readInt32(exception); + pts_count = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeInt32(date); + media.serializeToStream(stream); + stream.writeInt32(pts); + stream.writeInt32(pts_count); + } + } + + public static class TL_messages_sentMessageLink extends messages_SentMessage { + public static int constructor = 0x35a1a663; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + date = stream.readInt32(exception); + media = MessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); + pts = stream.readInt32(exception); + pts_count = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_contacts_link object = TL_contacts_link.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + links.add(object); + } + seq = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeInt32(date); + media.serializeToStream(stream); + stream.writeInt32(pts); + stream.writeInt32(pts_count); + stream.writeInt32(0x1cb5c415); + int count = links.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + links.get(a).serializeToStream(stream); + } + stream.writeInt32(seq); + } + } + + public static class TL_contactSuggested extends TLObject { + public static int constructor = 0x3de191a1; + + public int user_id; + public int mutual_contacts; + + public static TL_contactSuggested TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_contactSuggested.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_contactSuggested", constructor)); + } else { + return null; + } + } + TL_contactSuggested result = new TL_contactSuggested(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + mutual_contacts = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + stream.writeInt32(mutual_contacts); + } + } + + public static class InputChatPhoto extends TLObject { public InputPhoto id; + public InputPhotoCrop crop; + public InputFile file; - public void readParams(AbsSerializedData stream) { - id = (InputPhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public static InputChatPhoto TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputChatPhoto result = null; + switch(constructor) { + case 0xb2e1bf08: + result = new TL_inputChatPhoto(); + break; + case 0x1ca48f57: + result = new TL_inputChatPhotoEmpty(); + break; + case 0x94254732: + result = new TL_inputChatUploadedPhoto(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputChatPhoto", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inputChatPhoto extends InputChatPhoto { + public static int constructor = 0xb2e1bf08; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = InputPhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + crop = InputPhotoCrop.TLdeserialize(stream, stream.readInt32(exception), exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); id.serializeToStream(stream); + crop.serializeToStream(stream); + } + } + + public static class TL_inputChatPhotoEmpty extends InputChatPhoto { + public static int constructor = 0x1ca48f57; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputChatUploadedPhoto extends InputChatPhoto { + public static int constructor = 0x94254732; + + + public void readParams(AbsSerializedData stream, boolean exception) { + file = InputFile.TLdeserialize(stream, stream.readInt32(exception), exception); + crop = InputPhotoCrop.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + file.serializeToStream(stream); + crop.serializeToStream(stream); + } + } + + public static class InputPeer extends TLObject { + public int user_id; + public int chat_id; + public long access_hash; + + public static InputPeer TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputPeer result = null; + switch(constructor) { + case 0x1023dbe8: + result = new TL_inputPeerContact(); + break; + case 0x179be863: + result = new TL_inputPeerChat(); + break; + case 0x7f3b18ea: + result = new TL_inputPeerEmpty(); + break; + case 0x7da07ec9: + result = new TL_inputPeerSelf(); + break; + case 0x9b447325: + result = new TL_inputPeerForeign(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputPeer", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inputPeerContact extends InputPeer { + public static int constructor = 0x1023dbe8; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + } + } + + public static class TL_inputPeerChat extends InputPeer { + public static int constructor = 0x179be863; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + } + } + + public static class TL_inputPeerEmpty extends InputPeer { + public static int constructor = 0x7f3b18ea; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputPeerSelf extends InputPeer { + public static int constructor = 0x7da07ec9; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputPeerForeign extends InputPeer { + public static int constructor = 0x9b447325; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + access_hash = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + stream.writeInt64(access_hash); + } + } + + public static class TL_msg_copy extends TLObject { + public static int constructor = 0xe06046b2; + + public TL_protoMessage orig_message; + + public static TL_msg_copy TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_msg_copy.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_msg_copy", constructor)); + } else { + return null; + } + } + TL_msg_copy result = new TL_msg_copy(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + orig_message = TL_protoMessage.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + orig_message.serializeToStream(stream); + } + } + + public static class FileLocation extends TLObject { + public int dc_id; + public long volume_id; + public int local_id; + public long secret; + public byte[] key; + public byte[] iv; + + public static FileLocation TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + FileLocation result = null; + switch(constructor) { + case 0x53d69076: + result = new TL_fileLocation(); + break; + case 0x55555554: + result = new TL_fileEncryptedLocation(); + break; + case 0x7c596b46: + result = new TL_fileLocationUnavailable(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in FileLocation", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_fileLocation extends FileLocation { + public static int constructor = 0x53d69076; + + + public void readParams(AbsSerializedData stream, boolean exception) { + dc_id = stream.readInt32(exception); + volume_id = stream.readInt64(exception); + local_id = stream.readInt32(exception); + secret = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(dc_id); + stream.writeInt64(volume_id); + stream.writeInt32(local_id); + stream.writeInt64(secret); + } + } + + public static class TL_fileEncryptedLocation extends FileLocation { + public static int constructor = 0x55555554; + + + public void readParams(AbsSerializedData stream, boolean exception) { + dc_id = stream.readInt32(exception); + volume_id = stream.readInt64(exception); + local_id = stream.readInt32(exception); + secret = stream.readInt64(exception); + key = stream.readByteArray(exception); + iv = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(dc_id); + stream.writeInt64(volume_id); + stream.writeInt32(local_id); + stream.writeInt64(secret); + stream.writeByteArray(key); + stream.writeByteArray(iv); + } + } + + public static class TL_fileLocationUnavailable extends FileLocation { + public static int constructor = 0x7c596b46; + + + public void readParams(AbsSerializedData stream, boolean exception) { + volume_id = stream.readInt64(exception); + local_id = stream.readInt32(exception); + secret = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(volume_id); + stream.writeInt32(local_id); + stream.writeInt64(secret); + } + } + + public static class TL_stickerSet extends TLObject { + public static int constructor = 0xa7a43b17; + + public long id; + public long access_hash; + public String title; + public String short_name; + + public static TL_stickerSet TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_stickerSet.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_stickerSet", constructor)); + } else { + return null; + } + } + TL_stickerSet result = new TL_stickerSet(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + title = stream.readString(exception); + short_name = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeString(title); + stream.writeString(short_name); + } + } + + public static class TL_pong extends TLObject { + public static int constructor = 0x347773c5; + + public long msg_id; + public long ping_id; + + public static TL_pong TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_pong.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_pong", constructor)); + } else { + return null; + } + } + TL_pong result = new TL_pong(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + msg_id = stream.readInt64(exception); + ping_id = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(msg_id); + stream.writeInt64(ping_id); + } + } + + public static class TL_inputAppEvent extends TLObject { + public static int constructor = 0x770656a8; + + public double time; + public String type; + public long peer; + public String data; + + public static TL_inputAppEvent TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_inputAppEvent.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_inputAppEvent", constructor)); + } else { + return null; + } + } + TL_inputAppEvent result = new TL_inputAppEvent(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + time = stream.readDouble(exception); + type = stream.readString(exception); + peer = stream.readInt64(exception); + data = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeDouble(time); + stream.writeString(type); + stream.writeInt64(peer); + stream.writeString(data); + } + } + + public static class TL_messages_chatFull extends TLObject { + public static int constructor = 0xe5d7d19c; + + public TL_chatFull full_chat; + public ArrayList chats = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + + public static TL_messages_chatFull TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_messages_chatFull.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_messages_chatFull", constructor)); + } else { + return null; + } + } + TL_messages_chatFull result = new TL_messages_chatFull(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + full_chat = TL_chatFull.TLdeserialize(stream, stream.readInt32(exception), exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Chat object = Chat.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + chats.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + full_chat.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.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 InputNotifyPeer extends TLObject { + + public static InputNotifyPeer TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputNotifyPeer result = null; + switch(constructor) { + case 0x4a95e84e: + result = new TL_inputNotifyChats(); + break; + case 0xb8bc5b0c: + result = new TL_inputNotifyPeer(); + break; + case 0x193b4417: + result = new TL_inputNotifyUsers(); + break; + case 0x4d8ddec8: + result = new TL_inputNotifyGeoChatPeer(); + break; + case 0xa429b886: + result = new TL_inputNotifyAll(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputNotifyPeer", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inputNotifyChats extends InputNotifyPeer { + public static int constructor = 0x4a95e84e; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputNotifyPeer extends InputNotifyPeer { + public static int constructor = 0xb8bc5b0c; + + public InputPeer peer; + + public void readParams(AbsSerializedData stream, boolean exception) { + peer = InputPeer.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + peer.serializeToStream(stream); + } + } + + public static class TL_inputNotifyUsers extends InputNotifyPeer { + public static int constructor = 0x193b4417; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputNotifyGeoChatPeer extends InputNotifyPeer { + public static int constructor = 0x4d8ddec8; + + public TL_inputGeoChat peer; + + public void readParams(AbsSerializedData stream, boolean exception) { + peer = TL_inputGeoChat.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + peer.serializeToStream(stream); + } + } + + public static class TL_inputNotifyAll extends InputNotifyPeer { + public static int constructor = 0xa429b886; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_messages_affectedHistory extends TLObject { + public static int constructor = 0xb45c69d1; + + public int pts; + public int pts_count; + public int offset; + + public static TL_messages_affectedHistory TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_messages_affectedHistory.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_messages_affectedHistory", constructor)); + } else { + return null; + } + } + TL_messages_affectedHistory result = new TL_messages_affectedHistory(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + pts = stream.readInt32(exception); + pts_count = stream.readInt32(exception); + offset = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(pts); + stream.writeInt32(pts_count); + stream.writeInt32(offset); + } + } + + public static class RpcDropAnswer extends TLObject { + public long msg_id; + public int seq_no; + public int bytes; + + public static RpcDropAnswer TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + RpcDropAnswer result = null; + switch(constructor) { + case 0x5e2ad36e: + result = new TL_rpc_answer_unknown(); + break; + case 0xa43ad8b7: + result = new TL_rpc_answer_dropped(); + break; + case 0xcd78e586: + result = new TL_rpc_answer_dropped_running(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in RpcDropAnswer", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_rpc_answer_unknown extends RpcDropAnswer { + public static int constructor = 0x5e2ad36e; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_rpc_answer_dropped extends RpcDropAnswer { + public static int constructor = 0xa43ad8b7; + + + public void readParams(AbsSerializedData stream, boolean exception) { + msg_id = stream.readInt64(exception); + seq_no = stream.readInt32(exception); + bytes = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(msg_id); + stream.writeInt32(seq_no); + stream.writeInt32(bytes); + } + } + + public static class TL_rpc_answer_dropped_running extends RpcDropAnswer { + public static int constructor = 0xcd78e586; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class InputVideo extends TLObject { + public long id; + public long access_hash; + + public static InputVideo TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputVideo result = null; + switch(constructor) { + case 0x5508ec75: + result = new TL_inputVideoEmpty(); + break; + case 0xee579652: + result = new TL_inputVideo(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputVideo", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inputVideoEmpty extends InputVideo { + public static int constructor = 0x5508ec75; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputVideo extends InputVideo { + public static int constructor = 0xee579652; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + } + } + + public static class messages_DhConfig extends TLObject { + public byte[] random; + public int g; + public byte[] p; + public int version; + + public static messages_DhConfig TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + messages_DhConfig result = null; + switch(constructor) { + case 0xc0e24635: + result = new TL_messages_dhConfigNotModified(); + break; + case 0x2c221edd: + result = new TL_messages_dhConfig(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in messages_DhConfig", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_messages_dhConfigNotModified extends messages_DhConfig { + public static int constructor = 0xc0e24635; + + + public void readParams(AbsSerializedData stream, boolean exception) { + random = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeByteArray(random); + } + } + + public static class TL_messages_dhConfig extends messages_DhConfig { + public static int constructor = 0x2c221edd; + + + public void readParams(AbsSerializedData stream, boolean exception) { + g = stream.readInt32(exception); + p = stream.readByteArray(exception); + version = stream.readInt32(exception); + random = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(g); + stream.writeByteArray(p); + stream.writeInt32(version); + stream.writeByteArray(random); + } + } + + public static class Peer extends TLObject { + public int user_id; + public int chat_id; + + public static Peer TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + Peer result = null; + switch(constructor) { + case 0x9db1bc6d: + result = new TL_peerUser(); + break; + case 0xbad0e5bb: + result = new TL_peerChat(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in Peer", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_peerUser extends Peer { + public static int constructor = 0x9db1bc6d; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + } + } + + public static class TL_peerChat extends Peer { + public static int constructor = 0xbad0e5bb; + + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + } + } + + public static class TL_dcOption extends TLObject { + public static int constructor = 0x5d8c6cc; + + public int flags; + public int id; + public String ip_address; + public int port; + + public static TL_dcOption TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_dcOption.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_dcOption", constructor)); + } else { + return null; + } + } + TL_dcOption result = new TL_dcOption(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + id = stream.readInt32(exception); + ip_address = stream.readString(exception); + port = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(flags); + stream.writeInt32(id); + stream.writeString(ip_address); + stream.writeInt32(port); + } + } + + public static class InputFile extends TLObject { + public long id; + public int parts; + public String name; + public String md5_checksum; + + public static InputFile TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputFile result = null; + switch(constructor) { + case 0xfa4f0bb5: + result = new TL_inputFileBig(); + break; + case 0xf52ff27f: + result = new TL_inputFile(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputFile", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inputFileBig extends InputFile { + public static int constructor = 0xfa4f0bb5; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + parts = stream.readInt32(exception); + name = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt32(parts); + stream.writeString(name); + } + } + + public static class TL_inputFile extends InputFile { + public static int constructor = 0xf52ff27f; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + parts = stream.readInt32(exception); + name = stream.readString(exception); + md5_checksum = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt32(parts); + stream.writeString(name); + stream.writeString(md5_checksum); + } + } + + public static class TL_account_passwordInputSettings extends TLObject { + public static int constructor = 0xbcfc532c; + + public int flags; + public byte[] new_salt; + public byte[] new_password_hash; + public String hint; + public String email; + + public static TL_account_passwordInputSettings TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_account_passwordInputSettings.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_account_passwordInputSettings", constructor)); + } else { + return null; + } + } + TL_account_passwordInputSettings result = new TL_account_passwordInputSettings(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + if ((flags & 1) != 0) { + new_salt = stream.readByteArray(exception); + } + if ((flags & 1) != 0) { + new_password_hash = stream.readByteArray(exception); + } + if ((flags & 1) != 0) { + hint = stream.readString(exception); + } + if ((flags & 2) != 0) { + email = stream.readString(exception); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(flags); + if ((flags & 1) != 0) { + stream.writeByteArray(new_salt); + } + if ((flags & 1) != 0) { + stream.writeByteArray(new_password_hash); + } + if ((flags & 1) != 0) { + stream.writeString(hint); + } + if ((flags & 2) != 0) { + stream.writeString(email); + } + } + } + + public static class InputUser extends TLObject { + public int user_id; + public long access_hash; + + public static InputUser TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputUser result = null; + switch(constructor) { + case 0xf7c1b13f: + result = new TL_inputUserSelf(); + break; + case 0x655e74ff: + result = new TL_inputUserForeign(); + break; + case 0xb98886cf: + result = new TL_inputUserEmpty(); + break; + case 0x86e94f65: + result = new TL_inputUserContact(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputUser", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inputUserSelf extends InputUser { + public static int constructor = 0xf7c1b13f; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputUserForeign extends InputUser { + public static int constructor = 0x655e74ff; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + access_hash = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + stream.writeInt64(access_hash); + } + } + + public static class TL_inputUserEmpty extends InputUser { + public static int constructor = 0xb98886cf; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputUserContact extends InputUser { + public static int constructor = 0x86e94f65; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + } + } + + public static class TL_chatParticipant extends TLObject { + public static int constructor = 0xc8d7493e; + + public int user_id; + public int inviter_id; + public int date; + + public static TL_chatParticipant TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_chatParticipant.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_chatParticipant", constructor)); + } else { + return null; + } + } + TL_chatParticipant result = new TL_chatParticipant(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + inviter_id = stream.readInt32(exception); + date = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + stream.writeInt32(inviter_id); + stream.writeInt32(date); + } + } + + public static class EncryptedFile extends TLObject { + public long id; + public long access_hash; + public int size; + public int dc_id; + public int key_fingerprint; + + public static EncryptedFile TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + EncryptedFile result = null; + switch(constructor) { + case 0x4a70994c: + result = new TL_encryptedFile(); + break; + case 0xc21f497e: + result = new TL_encryptedFileEmpty(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in EncryptedFile", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_encryptedFile extends EncryptedFile { + public static int constructor = 0x4a70994c; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + size = stream.readInt32(exception); + dc_id = stream.readInt32(exception); + key_fingerprint = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(size); + stream.writeInt32(dc_id); + stream.writeInt32(key_fingerprint); + } + } + + public static class TL_encryptedFileEmpty extends EncryptedFile { + public static int constructor = 0xc21f497e; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_account_privacyRules extends TLObject { + public static int constructor = 0x554abb6f; + + public ArrayList rules = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + + public static TL_account_privacyRules TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_account_privacyRules.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_account_privacyRules", constructor)); + } else { + return null; + } + } + TL_account_privacyRules result = new TL_account_privacyRules(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + PrivacyRule object = PrivacyRule.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + rules.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + } + + 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 TL_auth_exportedAuthorization extends TLObject { + public static int constructor = 0xdf969c2d; + + public int id; + public byte[] bytes; + + public static TL_auth_exportedAuthorization TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_auth_exportedAuthorization.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_auth_exportedAuthorization", constructor)); + } else { + return null; + } + } + TL_auth_exportedAuthorization result = new TL_auth_exportedAuthorization(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + bytes = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeByteArray(bytes); + } + } + + public static class InputFileLocation extends TLObject { + public long id; + public long access_hash; + public long volume_id; + public int local_id; + public long secret; + + public static InputFileLocation TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputFileLocation result = null; + switch(constructor) { + case 0x74dc404d: + result = new TL_inputAudioFileLocation(); + break; + case 0xf5235d55: + result = new TL_inputEncryptedFileLocation(); + break; + case 0x3d0364ec: + result = new TL_inputVideoFileLocation(); + break; + case 0x4e45abe9: + result = new TL_inputDocumentFileLocation(); + break; + case 0x14637196: + result = new TL_inputFileLocation(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputFileLocation", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inputAudioFileLocation extends InputFileLocation { + public static int constructor = 0x74dc404d; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + } + } + + public static class TL_inputEncryptedFileLocation extends InputFileLocation { + public static int constructor = 0xf5235d55; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + } + } + + public static class TL_inputVideoFileLocation extends InputFileLocation { + public static int constructor = 0x3d0364ec; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + } + } + + public static class TL_inputDocumentFileLocation extends InputFileLocation { + public static int constructor = 0x4e45abe9; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + } + } + + public static class TL_inputFileLocation extends InputFileLocation { + public static int constructor = 0x14637196; + + + public void readParams(AbsSerializedData stream, boolean exception) { + volume_id = stream.readInt64(exception); + local_id = stream.readInt32(exception); + secret = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(volume_id); + stream.writeInt32(local_id); + stream.writeInt64(secret); + } + } + + public static class TL_chatFull extends TLObject { + public static int constructor = 0xcade0791; + + public int id; + public ChatParticipants participants; + public Photo chat_photo; + public PeerNotifySettings notify_settings; + public ExportedChatInvite exported_invite; + + public static TL_chatFull TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_chatFull.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_chatFull", constructor)); + } else { + return null; + } + } + TL_chatFull result = new TL_chatFull(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + participants = ChatParticipants.TLdeserialize(stream, stream.readInt32(exception), exception); + chat_photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + notify_settings = PeerNotifySettings.TLdeserialize(stream, stream.readInt32(exception), exception); + exported_invite = ExportedChatInvite.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + participants.serializeToStream(stream); + chat_photo.serializeToStream(stream); + notify_settings.serializeToStream(stream); + exported_invite.serializeToStream(stream); + } + } + + public static class InputGeoPoint extends TLObject { + public double lat; + public double _long; + + public static InputGeoPoint TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputGeoPoint result = null; + switch(constructor) { + case 0xf3b7acc9: + result = new TL_inputGeoPoint(); + break; + case 0xe4c123d6: + result = new TL_inputGeoPointEmpty(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputGeoPoint", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inputGeoPoint extends InputGeoPoint { + public static int constructor = 0xf3b7acc9; + + + public void readParams(AbsSerializedData stream, boolean exception) { + lat = stream.readDouble(exception); + _long = stream.readDouble(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeDouble(lat); + stream.writeDouble(_long); + } + } + + public static class TL_inputGeoPointEmpty extends InputGeoPoint { + public static int constructor = 0xe4c123d6; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class Photo extends TLObject { + public long id; + public long access_hash; + public int user_id; + public int date; + public GeoPoint geo; + public ArrayList sizes = new ArrayList<>(); + public String caption; + + public static Photo TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + Photo result = null; + switch(constructor) { + case 0x2331b22d: + result = new TL_photoEmpty(); + break; + case 0xc3838076: + result = new TL_photo(); + break; + case 0x22b56751: + result = new TL_photo_old(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in Photo", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_photoEmpty extends Photo { + public static int constructor = 0x2331b22d; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + } + } + + public static class TL_photo extends Photo { + public static int constructor = 0xc3838076; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); + geo = GeoPoint.TLdeserialize(stream, stream.readInt32(exception), exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + PhotoSize object = PhotoSize.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + sizes.add(object); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(user_id); + stream.writeInt32(date); + geo.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = sizes.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + sizes.get(a).serializeToStream(stream); + } + } + } + + public static class TL_photo_old extends TL_photo { + public static int constructor = 0x22b56751; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); + caption = stream.readString(exception); + geo = GeoPoint.TLdeserialize(stream, stream.readInt32(exception), exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + PhotoSize object = PhotoSize.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + sizes.add(object); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(user_id); + stream.writeInt32(date); + stream.writeString(caption); + geo.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = sizes.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + sizes.get(a).serializeToStream(stream); + } + } + } + + public static class TL_help_support extends TLObject { + public static int constructor = 0x17c6b5f6; + + public String phone_number; + public User user; + + public static TL_help_support TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_help_support.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_help_support", constructor)); + } else { + return null; + } + } + TL_help_support result = new TL_help_support(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + phone_number = stream.readString(exception); + user = User.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(phone_number); + user.serializeToStream(stream); + } + } + + public static class TL_inputPhoneContact extends TLObject { + public static int constructor = 0xf392b7f4; + + public long client_id; + public String phone; + public String first_name; + public String last_name; + + public static TL_inputPhoneContact TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_inputPhoneContact.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_inputPhoneContact", constructor)); + } else { + return null; + } + } + TL_inputPhoneContact result = new TL_inputPhoneContact(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + client_id = stream.readInt64(exception); + phone = stream.readString(exception); + first_name = stream.readString(exception); + last_name = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(client_id); + stream.writeString(phone); + stream.writeString(first_name); + stream.writeString(last_name); + } + } + + public static class Bool extends TLObject { + + public static Bool TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + Bool result = null; + switch(constructor) { + case 0x997275b5: + result = new TL_boolTrue(); + break; + case 0xbc799737: + result = new TL_boolFalse(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in Bool", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_boolTrue extends Bool { + public static int constructor = 0x997275b5; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_boolFalse extends Bool { + public static int constructor = 0xbc799737; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class EncryptedMessage extends TLObject { + public long random_id; + public int chat_id; + public int date; + public byte[] bytes; + public EncryptedFile file; + + public static EncryptedMessage TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + EncryptedMessage result = null; + switch(constructor) { + case 0x23734b06: + result = new TL_encryptedMessageService(); + break; + case 0xed18c118: + result = new TL_encryptedMessage(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in EncryptedMessage", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_encryptedMessageService extends EncryptedMessage { + public static int constructor = 0x23734b06; + + + public void readParams(AbsSerializedData stream, boolean exception) { + random_id = stream.readInt64(exception); + chat_id = stream.readInt32(exception); + date = stream.readInt32(exception); + bytes = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(random_id); + stream.writeInt32(chat_id); + stream.writeInt32(date); + stream.writeByteArray(bytes); + } + } + + public static class TL_encryptedMessage extends EncryptedMessage { + public static int constructor = 0xed18c118; + + + public void readParams(AbsSerializedData stream, boolean exception) { + random_id = stream.readInt64(exception); + chat_id = stream.readInt32(exception); + date = stream.readInt32(exception); + bytes = stream.readByteArray(exception); + file = EncryptedFile.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(random_id); + stream.writeInt32(chat_id); + stream.writeInt32(date); + stream.writeByteArray(bytes); + file.serializeToStream(stream); + } + } + + public static class TL_messages_messageEmpty extends TLObject { + public static int constructor = 0x3f4e0648; + + + public static TL_messages_messageEmpty TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_messages_messageEmpty.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_messages_messageEmpty", constructor)); + } else { + return null; + } + } + TL_messages_messageEmpty result = new TL_messages_messageEmpty(); + result.readParams(stream, exception); + return result; + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class DestroySessionRes extends TLObject { + public long session_id; + + public static DestroySessionRes TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + DestroySessionRes result = null; + switch(constructor) { + case 0xe22045fc: + result = new TL_destroy_session_ok(); + break; + case 0x62d350c9: + result = new TL_destroy_session_none(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in DestroySessionRes", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_destroy_session_ok extends DestroySessionRes { + public static int constructor = 0xe22045fc; + + + public void readParams(AbsSerializedData stream, boolean exception) { + session_id = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(session_id); + } + } + + public static class TL_destroy_session_none extends DestroySessionRes { + public static int constructor = 0x62d350c9; + + + public void readParams(AbsSerializedData stream, boolean exception) { + session_id = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(session_id); + } + } + + public static class PhotoSize extends TLObject { + public String type; + public FileLocation location; + public int w; + public int h; + public int size; + public byte[] bytes; + + public static PhotoSize TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + PhotoSize result = null; + switch(constructor) { + case 0x77bfb61b: + result = new TL_photoSize(); + break; + case 0xe17e23c: + result = new TL_photoSizeEmpty(); + break; + case 0xe9a734fa: + result = new TL_photoCachedSize(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in PhotoSize", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_photoSize extends PhotoSize { + public static int constructor = 0x77bfb61b; + + + public void readParams(AbsSerializedData stream, boolean exception) { + type = stream.readString(exception); + location = FileLocation.TLdeserialize(stream, stream.readInt32(exception), exception); + w = stream.readInt32(exception); + h = stream.readInt32(exception); + size = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(type); + location.serializeToStream(stream); + stream.writeInt32(w); + stream.writeInt32(h); + stream.writeInt32(size); + } + } + + public static class TL_photoSizeEmpty extends PhotoSize { + public static int constructor = 0xe17e23c; + + + public void readParams(AbsSerializedData stream, boolean exception) { + type = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(type); + } + } + + public static class TL_photoCachedSize extends PhotoSize { + public static int constructor = 0xe9a734fa; + + + public void readParams(AbsSerializedData stream, boolean exception) { + type = stream.readString(exception); + location = FileLocation.TLdeserialize(stream, stream.readInt32(exception), exception); + w = stream.readInt32(exception); + h = stream.readInt32(exception); + bytes = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(type); + location.serializeToStream(stream); + stream.writeInt32(w); + stream.writeInt32(h); + stream.writeByteArray(bytes); + } + } + + public static class MessageAction extends TLObject { + public String title; + public String address; + public int user_id; + public int inviter_id; + public DecryptedMessageAction encryptedAction; + public int ttl; + public UserProfilePhoto newUserPhoto; + public Photo photo; + public ArrayList users = new ArrayList<>(); + + public static MessageAction TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + MessageAction result = null; + switch(constructor) { + case 0xc7d53de: + result = new TL_messageActionGeoChatCheckin(); + break; + case 0x55555557: + result = new TL_messageActionCreatedBroadcastList(); + break; + case 0xb5a1ce5a: + result = new TL_messageActionChatEditTitle(); + break; + case 0x555555F5: + result = new TL_messageActionLoginUnknownLocation(); + break; + case 0x5e3cfc4b: + result = new TL_messageActionChatAddUser(); + break; + case 0xf89cf5e8: + result = new TL_messageActionChatJoinedByLink(); + break; + case 0x55555550: + result = new TL_messageActionUserJoined(); + break; + case 0x555555F7: + result = new TL_messageEncryptedAction(); + break; + case 0x55555552: + result = new TL_messageActionTTLChange(); + break; + case 0x55555551: + result = new TL_messageActionUserUpdatedPhoto(); + break; + case 0xb6aef7b0: + result = new TL_messageActionEmpty(); + break; + case 0x95e3fbef: + result = new TL_messageActionChatDeletePhoto(); + break; + case 0xb2ae9b0c: + result = new TL_messageActionChatDeleteUser(); + break; + case 0x7fcb13a8: + result = new TL_messageActionChatEditPhoto(); + break; + case 0xa6638b9a: + result = new TL_messageActionChatCreate(); + break; + case 0x6f038ebc: + result = new TL_messageActionGeoChatCreate(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in MessageAction", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_messageActionGeoChatCheckin extends MessageAction { + public static int constructor = 0xc7d53de; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_messageActionCreatedBroadcastList extends MessageAction { + public static int constructor = 0x55555557; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_messageActionChatEditTitle extends MessageAction { + public static int constructor = 0xb5a1ce5a; + + + public void readParams(AbsSerializedData stream, boolean exception) { + title = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(title); + } + } + + public static class TL_messageActionLoginUnknownLocation extends MessageAction { + public static int constructor = 0x555555F5; + + + public void readParams(AbsSerializedData stream, boolean exception) { + title = stream.readString(exception); + address = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(title); + stream.writeString(address); + } + } + + public static class TL_messageActionChatAddUser extends MessageAction { + public static int constructor = 0x5e3cfc4b; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + } + } + + public static class TL_messageActionChatJoinedByLink extends MessageAction { + public static int constructor = 0xf89cf5e8; + + + public void readParams(AbsSerializedData stream, boolean exception) { + inviter_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(inviter_id); + } + } + + public static class TL_messageActionUserJoined extends MessageAction { + public static int constructor = 0x55555550; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_messageEncryptedAction extends MessageAction { + public static int constructor = 0x555555F7; + + + public void readParams(AbsSerializedData stream, boolean exception) { + encryptedAction = DecryptedMessageAction.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + encryptedAction.serializeToStream(stream); + } + } + + public static class TL_messageActionTTLChange extends MessageAction { + public static int constructor = 0x55555552; + + + public void readParams(AbsSerializedData stream, boolean exception) { + ttl = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(ttl); + } + } + + public static class TL_messageActionUserUpdatedPhoto extends MessageAction { + public static int constructor = 0x55555551; + + + public void readParams(AbsSerializedData stream, boolean exception) { + newUserPhoto = UserProfilePhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + newUserPhoto.serializeToStream(stream); + } + } + + public static class TL_messageActionEmpty extends MessageAction { + public static int constructor = 0xb6aef7b0; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_messageActionChatDeletePhoto extends MessageAction { + public static int constructor = 0x95e3fbef; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_messageActionChatDeleteUser extends MessageAction { + public static int constructor = 0xb2ae9b0c; + + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + } + } + + public static class TL_messageActionChatEditPhoto extends MessageAction { + public static int constructor = 0x7fcb13a8; + + + public void readParams(AbsSerializedData stream, boolean exception) { + photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + photo.serializeToStream(stream); + } + } + + public static class TL_messageActionChatCreate extends MessageAction { + public static int constructor = 0xa6638b9a; + + + public void readParams(AbsSerializedData stream, boolean exception) { + title = stream.readString(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + users.add(stream.readInt32(exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(title); + stream.writeInt32(0x1cb5c415); + int count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt32(users.get(a)); + } + } + } + + public static class TL_messageActionGeoChatCreate extends MessageAction { + public static int constructor = 0x6f038ebc; + + + public void readParams(AbsSerializedData stream, boolean exception) { + title = stream.readString(exception); + address = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(title); + stream.writeString(address); + } + } + + public static class DecryptedMessageAction extends TLObject { + public int ttl_seconds; + public int layer; + public ArrayList random_ids = new ArrayList<>(); + public long exchange_id; + public long key_fingerprint; + public SendMessageAction action; + public byte[] g_b; + public int start_seq_no; + public int end_seq_no; + public byte[] g_a; + + public static DecryptedMessageAction TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + DecryptedMessageAction result = null; + switch(constructor) { + case 0xa1733aec: + result = new TL_decryptedMessageActionSetMessageTTL(); + break; + case 0xf3048883: + result = new TL_decryptedMessageActionNotifyLayer(); + break; + case 0x65614304: + result = new TL_decryptedMessageActionDeleteMessages(); + break; + case 0xec2e0b9b: + result = new TL_decryptedMessageActionCommitKey(); + break; + case 0xdd05ec6b: + result = new TL_decryptedMessageActionAbortKey(); + break; + case 0x6719e45c: + result = new TL_decryptedMessageActionFlushHistory(); + break; + case 0xccb27641: + result = new TL_decryptedMessageActionTyping(); + break; + case 0x6fe1735b: + result = new TL_decryptedMessageActionAcceptKey(); + break; + case 0xc4f40be: + result = new TL_decryptedMessageActionReadMessages(); + break; + case 0x511110b0: + result = new TL_decryptedMessageActionResend(); + break; + case 0xf3c9611b: + result = new TL_decryptedMessageActionRequestKey(); + break; + case 0x8ac1f475: + result = new TL_decryptedMessageActionScreenshotMessages(); + break; + case 0xa82fdd63: + result = new TL_decryptedMessageActionNoop(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in DecryptedMessageAction", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_decryptedMessageActionSetMessageTTL extends DecryptedMessageAction { + public static int constructor = 0xa1733aec; + + + public void readParams(AbsSerializedData stream, boolean exception) { + ttl_seconds = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(ttl_seconds); + } + } + + public static class TL_decryptedMessageActionNotifyLayer extends DecryptedMessageAction { + public static int constructor = 0xf3048883; + + + public void readParams(AbsSerializedData stream, boolean exception) { + layer = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(layer); + } + } + + public static class TL_decryptedMessageActionDeleteMessages extends DecryptedMessageAction { + public static int constructor = 0x65614304; + + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + random_ids.add(stream.readInt64(exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = random_ids.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt64(random_ids.get(a)); + } + } + } + + public static class TL_decryptedMessageActionCommitKey extends DecryptedMessageAction { + public static int constructor = 0xec2e0b9b; + + + public void readParams(AbsSerializedData stream, boolean exception) { + exchange_id = stream.readInt64(exception); + key_fingerprint = stream.readInt64(exception); + } + + 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, boolean exception) { + exchange_id = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(exchange_id); + } + } + + public static class TL_decryptedMessageActionFlushHistory extends DecryptedMessageAction { + public static int constructor = 0x6719e45c; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_decryptedMessageActionTyping extends DecryptedMessageAction { + public static int constructor = 0xccb27641; + + + public void readParams(AbsSerializedData stream, boolean exception) { + action = SendMessageAction.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + action.serializeToStream(stream); + } + } + + public static class TL_decryptedMessageActionAcceptKey extends DecryptedMessageAction { + public static int constructor = 0x6fe1735b; + + + public void readParams(AbsSerializedData stream, boolean exception) { + exchange_id = stream.readInt64(exception); + g_b = stream.readByteArray(exception); + key_fingerprint = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(exchange_id); + stream.writeByteArray(g_b); + stream.writeInt64(key_fingerprint); + } + } + + public static class TL_decryptedMessageActionReadMessages extends DecryptedMessageAction { + public static int constructor = 0xc4f40be; + + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + random_ids.add(stream.readInt64(exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = random_ids.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt64(random_ids.get(a)); + } + } + } + + public static class TL_decryptedMessageActionResend extends DecryptedMessageAction { + public static int constructor = 0x511110b0; + + + public void readParams(AbsSerializedData stream, boolean exception) { + start_seq_no = stream.readInt32(exception); + end_seq_no = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(start_seq_no); + stream.writeInt32(end_seq_no); + } + } + + public static class TL_decryptedMessageActionRequestKey extends DecryptedMessageAction { + public static int constructor = 0xf3c9611b; + + + public void readParams(AbsSerializedData stream, boolean exception) { + exchange_id = stream.readInt64(exception); + g_a = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(exchange_id); + stream.writeByteArray(g_a); + } + } + + public static class TL_decryptedMessageActionScreenshotMessages extends DecryptedMessageAction { + public static int constructor = 0x8ac1f475; + + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + random_ids.add(stream.readInt64(exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = random_ids.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt64(random_ids.get(a)); + } + } + } + + public static class TL_decryptedMessageActionNoop extends DecryptedMessageAction { + public static int constructor = 0xa82fdd63; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputGeoChat extends TLObject { + public static int constructor = 0x74d456fa; + + public int chat_id; + public long access_hash; + + public static TL_inputGeoChat TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_inputGeoChat.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_inputGeoChat", constructor)); + } else { + return null; + } + } + TL_inputGeoChat result = new TL_inputGeoChat(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + chat_id = stream.readInt32(exception); + access_hash = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + stream.writeInt64(access_hash); + } + } + + public static class TL_msgs_state_req extends TLObject { + public static int constructor = 0xda69fb52; + + public ArrayList msg_ids = new ArrayList<>(); + + public static TL_msgs_state_req TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_msgs_state_req.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_msgs_state_req", constructor)); + } else { + return null; + } + } + TL_msgs_state_req result = new TL_msgs_state_req(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + msg_ids.add(stream.readInt64(exception)); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = msg_ids.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt64(msg_ids.get(a)); + } + } + } + + public static class TL_importedContact extends TLObject { + public static int constructor = 0xd0028438; + + public int user_id; + public long client_id; + + public static TL_importedContact TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_importedContact.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_importedContact", constructor)); + } else { + return null; + } + } + TL_importedContact result = new TL_importedContact(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + client_id = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(user_id); + stream.writeInt64(client_id); + } + } + + public static class auth_SentCode extends TLObject { + public boolean phone_registered; + public String phone_code_hash; + public int send_call_timeout; + public boolean is_password; + + public static auth_SentCode TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + auth_SentCode result = null; + switch(constructor) { + case 0xe325edcf: + result = new TL_auth_sentAppCode(); + break; + case 0xefed51d9: + result = new TL_auth_sentCode(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in auth_SentCode", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_auth_sentAppCode extends auth_SentCode { + public static int constructor = 0xe325edcf; + + + public void readParams(AbsSerializedData stream, boolean exception) { + phone_registered = stream.readBool(exception); + phone_code_hash = stream.readString(exception); + send_call_timeout = stream.readInt32(exception); + is_password = stream.readBool(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeBool(phone_registered); + stream.writeString(phone_code_hash); + stream.writeInt32(send_call_timeout); + stream.writeBool(is_password); + } + } + + public static class TL_auth_sentCode extends auth_SentCode { + public static int constructor = 0xefed51d9; + + + public void readParams(AbsSerializedData stream, boolean exception) { + phone_registered = stream.readBool(exception); + phone_code_hash = stream.readString(exception); + send_call_timeout = stream.readInt32(exception); + is_password = stream.readBool(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeBool(phone_registered); + stream.writeString(phone_code_hash); + stream.writeInt32(send_call_timeout); + stream.writeBool(is_password); + } + } + + public static class TL_help_inviteText extends TLObject { + public static int constructor = 0x18cb9f78; + + public String message; + + public static TL_help_inviteText TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_help_inviteText.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_help_inviteText", constructor)); + } else { + return null; + } + } + TL_help_inviteText result = new TL_help_inviteText(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + message = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(message); + } + } + + public static class messages_AllStickers extends TLObject { + public String hash; + public ArrayList packs = new ArrayList<>(); + public ArrayList sets = new ArrayList<>(); + public ArrayList documents = new ArrayList<>(); + + public static messages_AllStickers TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + messages_AllStickers result = null; + switch(constructor) { + case 0x5ce352ec: + result = new TL_messages_allStickers(); + break; + case 0xe86602c3: + result = new TL_messages_allStickersNotModified(); + break; + case 0xdcef3102: + result = new TL_messages_allStickers_old(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in messages_AllStickers", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_messages_allStickers extends messages_AllStickers { + public static int constructor = 0x5ce352ec; + + + public void readParams(AbsSerializedData stream, boolean exception) { + hash = stream.readString(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_stickerPack object = TL_stickerPack.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + packs.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_stickerSet object = TL_stickerSet.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + sets.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Document object = Document.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + documents.add(object); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(hash); + stream.writeInt32(0x1cb5c415); + int count = packs.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + packs.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = sets.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + sets.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = documents.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + documents.get(a).serializeToStream(stream); + } + } + } + + public static class TL_messages_allStickersNotModified extends messages_AllStickers { + public static int constructor = 0xe86602c3; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_messages_allStickers_old extends TL_messages_allStickers { + public static int constructor = 0xdcef3102; + + + public void readParams(AbsSerializedData stream, boolean exception) { + hash = stream.readString(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_stickerPack object = TL_stickerPack.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + packs.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Document object = Document.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + documents.add(object); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(hash); + stream.writeInt32(0x1cb5c415); + int count = packs.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + packs.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = documents.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + documents.get(a).serializeToStream(stream); + } + } + } + + public static class TL_auth_checkedPhone extends TLObject { + public static int constructor = 0x811ea28e; + + public boolean phone_registered; + + public static TL_auth_checkedPhone TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_auth_checkedPhone.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_auth_checkedPhone", constructor)); + } else { + return null; + } + } + TL_auth_checkedPhone result = new TL_auth_checkedPhone(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + phone_registered = stream.readBool(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeBool(phone_registered); + } + } + + public static class UserProfilePhoto extends TLObject { + public long photo_id; + public FileLocation photo_small; + public FileLocation photo_big; + + public static UserProfilePhoto TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + UserProfilePhoto result = null; + switch(constructor) { + case 0x4f11bae1: + result = new TL_userProfilePhotoEmpty(); + break; + case 0xd559d8c8: + result = new TL_userProfilePhoto(); + break; + case 0x990d1493: + result = new TL_userProfilePhoto_old(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in UserProfilePhoto", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_userProfilePhotoEmpty extends UserProfilePhoto { + public static int constructor = 0x4f11bae1; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_userProfilePhoto extends UserProfilePhoto { + public static int constructor = 0xd559d8c8; + + + public void readParams(AbsSerializedData stream, boolean exception) { + photo_id = stream.readInt64(exception); + photo_small = FileLocation.TLdeserialize(stream, stream.readInt32(exception), exception); + photo_big = FileLocation.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(photo_id); + photo_small.serializeToStream(stream); + photo_big.serializeToStream(stream); + } + } + + public static class TL_userProfilePhoto_old extends TL_userProfilePhoto { + public static int constructor = 0x990d1493; + + + public void readParams(AbsSerializedData stream, boolean exception) { + photo_small = FileLocation.TLdeserialize(stream, stream.readInt32(exception), exception); + photo_big = FileLocation.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + photo_small.serializeToStream(stream); + photo_big.serializeToStream(stream); + } + } + + public static class TL_authorization extends TLObject { + public static int constructor = 0x7bf2e6f6; + + public long hash; + public int flags; + public String device_model; + public String platform; + public String system_version; + public int api_id; + public String app_name; + public String app_version; + public int date_created; + public int date_active; + public String ip; + public String country; + public String region; + + public static TL_authorization TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_authorization.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_authorization", constructor)); + } else { + return null; + } + } + TL_authorization result = new TL_authorization(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + hash = stream.readInt64(exception); + flags = stream.readInt32(exception); + device_model = stream.readString(exception); + platform = stream.readString(exception); + system_version = stream.readString(exception); + api_id = stream.readInt32(exception); + app_name = stream.readString(exception); + app_version = stream.readString(exception); + date_created = stream.readInt32(exception); + date_active = stream.readInt32(exception); + ip = stream.readString(exception); + country = stream.readString(exception); + region = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(hash); + stream.writeInt32(flags); + stream.writeString(device_model); + stream.writeString(platform); + stream.writeString(system_version); + stream.writeInt32(api_id); + stream.writeString(app_name); + stream.writeString(app_version); + stream.writeInt32(date_created); + stream.writeInt32(date_active); + stream.writeString(ip); + stream.writeString(country); + stream.writeString(region); + } + } + + public static class Server_DH_Params extends TLObject { + public byte[] nonce; + public byte[] server_nonce; + public byte[] new_nonce_hash; + public byte[] encrypted_answer; + + public static Server_DH_Params TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + Server_DH_Params result = null; + switch(constructor) { + case 0x79cb045d: + result = new TL_server_DH_params_fail(); + break; + case 0xd0e8075c: + result = new TL_server_DH_params_ok(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in Server_DH_Params", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_server_DH_params_fail extends Server_DH_Params { + public static int constructor = 0x79cb045d; + + + public void readParams(AbsSerializedData stream, boolean exception) { + nonce = stream.readData(16, exception); + server_nonce = stream.readData(16, exception); + new_nonce_hash = stream.readData(16, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeRaw(nonce); + stream.writeRaw(server_nonce); + stream.writeRaw(new_nonce_hash); + } + } + + public static class TL_server_DH_params_ok extends Server_DH_Params { + public static int constructor = 0xd0e8075c; + + + public void readParams(AbsSerializedData stream, boolean exception) { + nonce = stream.readData(16, exception); + server_nonce = stream.readData(16, exception); + encrypted_answer = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeRaw(nonce); + stream.writeRaw(server_nonce); + stream.writeByteArray(encrypted_answer); + } + } + + public static class TL_protoMessage extends TLObject { + public static int constructor = 0x5bb8e511; + + public long msg_id; + public int seqno; + public int bytes; + public TLObject body; + + public static TL_protoMessage TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_protoMessage.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_protoMessage", constructor)); + } else { + return null; + } + } + TL_protoMessage result = new TL_protoMessage(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + msg_id = stream.readInt64(exception); + seqno = stream.readInt32(exception); + bytes = stream.readInt32(exception); + body = TLClassStore.Instance().TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(msg_id); + stream.writeInt32(seqno); + stream.writeInt32(bytes); + body.serializeToStream(stream); + } + } + + public static class TL_geochats_located extends TLObject { + public static int constructor = 0x48feb267; + + public ArrayList results = new ArrayList<>(); + public ArrayList messages = new ArrayList<>(); + public ArrayList chats = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + + public static TL_geochats_located TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_geochats_located.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_geochats_located", constructor)); + } else { + return null; + } + } + TL_geochats_located result = new TL_geochats_located(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_chatLocated object = TL_chatLocated.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + results.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + GeoChatMessage object = GeoChatMessage.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + messages.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Chat object = Chat.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + chats.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = results.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + results.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = messages.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + messages.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.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 TL_msgs_all_info extends TLObject { + public static int constructor = 0x8cc0d131; + + public ArrayList msg_ids = new ArrayList<>(); + public String info; + + public static TL_msgs_all_info TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_msgs_all_info.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_msgs_all_info", constructor)); + } else { + return null; + } + } + TL_msgs_all_info result = new TL_msgs_all_info(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + msg_ids.add(stream.readInt64(exception)); + } + info = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = msg_ids.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt64(msg_ids.get(a)); + } + stream.writeString(info); + } + } + + public static class contacts_Blocked extends TLObject { + public ArrayList blocked = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + public int count; + + public static contacts_Blocked TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + contacts_Blocked result = null; + switch(constructor) { + case 0x1c138d15: + result = new TL_contacts_blocked(); + break; + case 0x900802a1: + result = new TL_contacts_blockedSlice(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in contacts_Blocked", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_contacts_blocked extends contacts_Blocked { + public static int constructor = 0x1c138d15; + + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_contactBlocked object = TL_contactBlocked.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + blocked.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = blocked.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + blocked.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 TL_contacts_blockedSlice extends contacts_Blocked { + public static int constructor = 0x900802a1; + + + public void readParams(AbsSerializedData stream, boolean exception) { + count = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_contactBlocked object = TL_contactBlocked.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + blocked.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(count); + stream.writeInt32(0x1cb5c415); + int count = blocked.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + blocked.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 TL_encryptedChatRequested_old extends TL_encryptedChatRequested { + public static int constructor = 0xfda9a7b7; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + access_hash = stream.readInt64(exception); + date = stream.readInt32(exception); + admin_id = stream.readInt32(exception); + participant_id = stream.readInt32(exception); + g_a = stream.readByteArray(exception); + nonce = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeInt64(access_hash); + stream.writeInt32(date); + stream.writeInt32(admin_id); + stream.writeInt32(participant_id); + stream.writeByteArray(g_a); + stream.writeByteArray(nonce); + } + } + + public static class TL_encryptedChatRequested extends EncryptedChat { + public static int constructor = 0xc878527e; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + access_hash = stream.readInt64(exception); + date = stream.readInt32(exception); + admin_id = stream.readInt32(exception); + participant_id = stream.readInt32(exception); + g_a = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeInt64(access_hash); + stream.writeInt32(date); + stream.writeInt32(admin_id); + stream.writeInt32(participant_id); + stream.writeByteArray(g_a); + } + } + + public static class TL_encryptedChat extends EncryptedChat { + public static int constructor = 0xfa56ce36; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + access_hash = stream.readInt64(exception); + date = stream.readInt32(exception); + admin_id = stream.readInt32(exception); + participant_id = stream.readInt32(exception); + g_a_or_b = stream.readByteArray(exception); + key_fingerprint = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeInt64(access_hash); + stream.writeInt32(date); + stream.writeInt32(admin_id); + stream.writeInt32(participant_id); + stream.writeByteArray(g_a_or_b); + stream.writeInt64(key_fingerprint); + } + } + + public static class TL_encryptedChat_old extends TL_encryptedChat { + public static int constructor = 0x6601d14f; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + access_hash = stream.readInt64(exception); + date = stream.readInt32(exception); + admin_id = stream.readInt32(exception); + participant_id = stream.readInt32(exception); + g_a_or_b = stream.readByteArray(exception); + nonce = stream.readByteArray(exception); + key_fingerprint = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeInt64(access_hash); + stream.writeInt32(date); + stream.writeInt32(admin_id); + stream.writeInt32(participant_id); + stream.writeByteArray(g_a_or_b); + stream.writeByteArray(nonce); + stream.writeInt64(key_fingerprint); + } + } + + public static class TL_encryptedChatEmpty extends EncryptedChat { + public static int constructor = 0xab7ec0a0; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + } + } + + public static class TL_encryptedChatWaiting extends EncryptedChat { + public static int constructor = 0x3bf703dc; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + access_hash = stream.readInt64(exception); + date = stream.readInt32(exception); + admin_id = stream.readInt32(exception); + participant_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeInt64(access_hash); + stream.writeInt32(date); + stream.writeInt32(admin_id); + stream.writeInt32(participant_id); + } + } + + public static class TL_encryptedChatDiscarded extends EncryptedChat { + public static int constructor = 0x13d6dd27; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + } + } + + public static class help_AppUpdate extends TLObject { + public int id; + public boolean critical; + public String url; + public String text; + + public static help_AppUpdate TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + help_AppUpdate result = null; + switch(constructor) { + case 0x8987f311: + result = new TL_help_appUpdate(); + break; + case 0xc45a6536: + result = new TL_help_noAppUpdate(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in help_AppUpdate", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_help_appUpdate extends help_AppUpdate { + public static int constructor = 0x8987f311; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + critical = stream.readBool(exception); + url = stream.readString(exception); + text = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeBool(critical); + stream.writeString(url); + stream.writeString(text); + } + } + + public static class TL_help_noAppUpdate extends help_AppUpdate { + public static int constructor = 0xc45a6536; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class messages_Stickers extends TLObject { + public String hash; + public ArrayList stickers = new ArrayList<>(); + + public static messages_Stickers TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + messages_Stickers result = null; + switch(constructor) { + case 0xf1749a22: + result = new TL_messages_stickersNotModified(); + break; + case 0x8a8ecd32: + result = new TL_messages_stickers(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in messages_Stickers", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_messages_stickersNotModified extends messages_Stickers { + public static int constructor = 0xf1749a22; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_messages_stickers extends messages_Stickers { + public static int constructor = 0x8a8ecd32; + + + public void readParams(AbsSerializedData stream, boolean exception) { + hash = stream.readString(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Document object = Document.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + stickers.add(object); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(hash); + stream.writeInt32(0x1cb5c415); + int count = stickers.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stickers.get(a).serializeToStream(stream); + } + } + } + + public static class Video extends TLObject { + public long id; + public long access_hash; + public int user_id; + public int date; + public int duration; + public int size; + public PhotoSize thumb; + public int dc_id; + public int w; + public int h; + public String caption; + public byte[] key; + public byte[] iv; + public String mime_type; + + public static Video TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + Video result = null; + switch(constructor) { + case 0xee9f4a4d: + result = new TL_video(); + break; + case 0x55555553: + result = new TL_videoEncrypted(); + break; + case 0x5a04a49f: + result = new TL_video_old(); + break; + case 0x388fa391: + result = new TL_video_old2(); + break; + case 0xc10658a8: + result = new TL_videoEmpty(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in Video", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_video extends Video { + public static int constructor = 0xee9f4a4d; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); + duration = stream.readInt32(exception); + size = stream.readInt32(exception); + thumb = PhotoSize.TLdeserialize(stream, stream.readInt32(exception), exception); + dc_id = stream.readInt32(exception); + w = stream.readInt32(exception); + h = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(user_id); + stream.writeInt32(date); + stream.writeInt32(duration); + stream.writeInt32(size); + thumb.serializeToStream(stream); + stream.writeInt32(dc_id); + stream.writeInt32(w); + stream.writeInt32(h); + } + } + + public static class TL_videoEncrypted extends TL_video { + public static int constructor = 0x55555553; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); + caption = stream.readString(exception); + duration = stream.readInt32(exception); + size = stream.readInt32(exception); + thumb = PhotoSize.TLdeserialize(stream, stream.readInt32(exception), exception); + dc_id = stream.readInt32(exception); + w = stream.readInt32(exception); + h = stream.readInt32(exception); + key = stream.readByteArray(exception); + iv = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(user_id); + stream.writeInt32(date); + stream.writeString(caption); + stream.writeInt32(duration); + stream.writeInt32(size); + thumb.serializeToStream(stream); + stream.writeInt32(dc_id); + stream.writeInt32(w); + stream.writeInt32(h); + stream.writeByteArray(key); + stream.writeByteArray(iv); + } + } + + public static class TL_video_old extends TL_video { + public static int constructor = 0x5a04a49f; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); + caption = stream.readString(exception); + duration = stream.readInt32(exception); + size = stream.readInt32(exception); + thumb = PhotoSize.TLdeserialize(stream, stream.readInt32(exception), exception); + dc_id = stream.readInt32(exception); + w = stream.readInt32(exception); + h = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(user_id); + stream.writeInt32(date); + stream.writeString(caption); + stream.writeInt32(duration); + stream.writeInt32(size); + thumb.serializeToStream(stream); + stream.writeInt32(dc_id); + stream.writeInt32(w); + stream.writeInt32(h); + } + } + + public static class TL_video_old2 extends TL_video { + public static int constructor = 0x388fa391; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); + caption = stream.readString(exception); + duration = stream.readInt32(exception); + mime_type = stream.readString(exception); + size = stream.readInt32(exception); + thumb = PhotoSize.TLdeserialize(stream, stream.readInt32(exception), exception); + dc_id = stream.readInt32(exception); + w = stream.readInt32(exception); + h = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(user_id); + stream.writeInt32(date); + stream.writeString(caption); + stream.writeInt32(duration); + stream.writeString(mime_type); + stream.writeInt32(size); + thumb.serializeToStream(stream); + stream.writeInt32(dc_id); + stream.writeInt32(w); + stream.writeInt32(h); + } + } + + public static class TL_videoEmpty extends Video { + public static int constructor = 0xc10658a8; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + } + } + + public static class TL_messages_affectedMessages extends TLObject { + public static int constructor = 0x84d19185; + + public int pts; + public int pts_count; + + public static TL_messages_affectedMessages TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_messages_affectedMessages.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_messages_affectedMessages", constructor)); + } else { + return null; + } + } + TL_messages_affectedMessages result = new TL_messages_affectedMessages(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + pts = stream.readInt32(exception); + pts_count = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(pts); + stream.writeInt32(pts_count); } } @@ -3540,28 +9249,77 @@ public class TLRPC { public ArrayList messages = new ArrayList<>(); public ArrayList chats = new ArrayList<>(); public ArrayList users = new ArrayList<>(); + + public static geochats_Messages TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + geochats_Messages result = null; + switch(constructor) { + case 0xbc5863e8: + result = new TL_geochats_messagesSlice(); + break; + case 0xd1526db1: + result = new TL_geochats_messages(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in geochats_Messages", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } } public static class TL_geochats_messagesSlice extends geochats_Messages { public static int constructor = 0xbc5863e8; - public void readParams(AbsSerializedData stream) { - count = stream.readInt32(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - messages.add((GeoChatMessage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + public void readParams(AbsSerializedData stream, boolean exception) { + count = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; } - stream.readInt32(); - count = stream.readInt32(); + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - chats.add((Chat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + GeoChatMessage object = GeoChatMessage.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + messages.add(object); } - stream.readInt32(); - count = stream.readInt32(); + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + Chat object = Chat.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + chats.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); } } @@ -3593,21 +9351,51 @@ public class TLRPC { public static int constructor = 0xd1526db1; - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - messages.add((GeoChatMessage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; } - stream.readInt32(); - count = stream.readInt32(); + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - chats.add((Chat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + GeoChatMessage object = GeoChatMessage.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + messages.add(object); } - stream.readInt32(); - count = stream.readInt32(); + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + Chat object = Chat.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + chats.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); } } @@ -3634,243 +9422,48 @@ public class TLRPC { } } - public static class messages_SentMessage extends TLObject { - public int id; - public int date; - public MessageMedia media; - public int pts; - public int pts_count; - public ArrayList links = new ArrayList<>(); - public int seq; - } + public static class TL_stickerPack extends TLObject { + public static int constructor = 0x12b299d4; - public static class TL_messages_sentMessage extends messages_SentMessage { - public static int constructor = 0x4c3d47f3; + public String emoticon; + public ArrayList documents = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - date = stream.readInt32(); - media = (MessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - pts = stream.readInt32(); - pts_count = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeInt32(date); - media.serializeToStream(stream); - stream.writeInt32(pts); - stream.writeInt32(pts_count); - } - } - - public static class TL_messages_sentMessageLink extends messages_SentMessage { - public static int constructor = 0x35a1a663; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - date = stream.readInt32(); - media = (MessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - pts = stream.readInt32(); - pts_count = stream.readInt32(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - links.add((TL_contacts_link)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + public static TL_stickerPack TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_stickerPack.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_stickerPack", constructor)); + } else { + return null; + } + } + TL_stickerPack result = new TL_stickerPack(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + emoticon = stream.readString(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + documents.add(stream.readInt64(exception)); } - seq = stream.readInt32(); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeInt32(date); - media.serializeToStream(stream); - stream.writeInt32(pts); - stream.writeInt32(pts_count); + stream.writeString(emoticon); stream.writeInt32(0x1cb5c415); - int count = links.size(); + int count = documents.size(); stream.writeInt32(count); for (int a = 0; a < count; a++) { - links.get(a).serializeToStream(stream); - } - stream.writeInt32(seq); - } - } - - public static class EncryptedMessage extends TLObject { - public long random_id; - public int chat_id; - public int date; - public byte[] bytes; - public EncryptedFile file; - } - - public static class TL_encryptedMessageService extends EncryptedMessage { - public static int constructor = 0x23734b06; - - - public void readParams(AbsSerializedData stream) { - random_id = stream.readInt64(); - chat_id = stream.readInt32(); - date = stream.readInt32(); - bytes = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(random_id); - stream.writeInt32(chat_id); - stream.writeInt32(date); - stream.writeByteArray(bytes); - } - } - - public static class TL_encryptedMessage extends EncryptedMessage { - public static int constructor = 0xed18c118; - - - public void readParams(AbsSerializedData stream) { - random_id = stream.readInt64(); - chat_id = stream.readInt32(); - date = stream.readInt32(); - bytes = stream.readByteArray(); - file = (EncryptedFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(random_id); - stream.writeInt32(chat_id); - stream.writeInt32(date); - stream.writeByteArray(bytes); - file.serializeToStream(stream); - } - } - - public static class TL_contactSuggested extends TLObject { - public static int constructor = 0x3de191a1; - - public int user_id; - public int mutual_contacts; - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - mutual_contacts = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - stream.writeInt32(mutual_contacts); - } - } - - public static class Server_DH_Params extends TLObject { - public byte[] nonce; - public byte[] server_nonce; - public byte[] new_nonce_hash; - public byte[] encrypted_answer; - } - - public static class TL_server_DH_params_fail extends Server_DH_Params { - public static int constructor = 0x79cb045d; - - - public void readParams(AbsSerializedData stream) { - nonce = stream.readData(16); - server_nonce = stream.readData(16); - new_nonce_hash = stream.readData(16); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeRaw(nonce); - stream.writeRaw(server_nonce); - stream.writeRaw(new_nonce_hash); - } - } - - public static class TL_server_DH_params_ok extends Server_DH_Params { - public static int constructor = 0xd0e8075c; - - - public void readParams(AbsSerializedData stream) { - nonce = stream.readData(16); - server_nonce = stream.readData(16); - encrypted_answer = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeRaw(nonce); - stream.writeRaw(server_nonce); - stream.writeByteArray(encrypted_answer); - } - } - - public static class TL_msg_copy extends TLObject { - public static int constructor = 0xe06046b2; - - public TL_protoMessage orig_message; - - public void readParams(AbsSerializedData stream) { - orig_message = (TL_protoMessage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - orig_message.serializeToStream(stream); - } - } - - public static class TL_contacts_importedContacts extends TLObject { - public static int constructor = 0xad524315; - - public ArrayList imported = new ArrayList<>(); - public ArrayList retry_contacts = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - imported.add((TL_importedContact)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - retry_contacts.add(stream.readInt64()); - } - 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 = imported.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - imported.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = retry_contacts.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - stream.writeInt64(retry_contacts.get(a)); - } - stream.writeInt32(0x1cb5c415); - count = users.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - users.get(a).serializeToStream(stream); + stream.writeInt64(documents.get(a)); } } } @@ -3881,9 +9474,22 @@ public class TLRPC { public String feature; public String description; - public void readParams(AbsSerializedData stream) { - feature = stream.readString(); - description = stream.readString(); + public static TL_disabledFeature TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_disabledFeature.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_disabledFeature", constructor)); + } else { + return null; + } + } + TL_disabledFeature result = new TL_disabledFeature(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + feature = stream.readString(exception); + description = stream.readString(exception); } public void serializeToStream(AbsSerializedData stream) { @@ -3893,1757 +9499,50 @@ public class TLRPC { } } - public static class TL_futureSalt extends TLObject { - public static int constructor = 0x0949d9dc; - - public int valid_since; - public int valid_until; - public long salt; - - public void readParams(AbsSerializedData stream) { - valid_since = stream.readInt32(); - valid_until = stream.readInt32(); - salt = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(valid_since); - stream.writeInt32(valid_until); - stream.writeInt64(salt); - } - } - - public static class Update extends TLObject { - public int chat_id; - public int max_date; - public int date; - public int user_id; - public ContactLink my_link; - public ContactLink foreign_link; - public ArrayList messages = new ArrayList<>(); - public int pts; - public int pts_count; - public int max_id; - public int version; - public WebPage webpage; - public String type; - public MessageMedia media; - public boolean popup; - public NotifyPeer peer; - public PeerNotifySettings notify_settings; - public SendMessageAction action; - public String first_name; - public String last_name; - public String username; - public String phone; - public int qts; - public int id; - public long random_id; - public ArrayList dc_options = new ArrayList<>(); - public ChatParticipants participants; - public TL_privacyKeyStatusTimestamp key; - public ArrayList rules = new ArrayList<>(); - public EncryptedChat chat; - public boolean blocked; - public long auth_key_id; - public String device; - public String location; - public UserProfilePhoto photo; - public boolean previous; - public int inviter_id; - public UserStatus status; - } - - public static class TL_updateEncryptedMessagesRead extends Update { - public static int constructor = 0x38fe25b7; - - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - max_date = stream.readInt32(); - date = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - stream.writeInt32(max_date); - stream.writeInt32(date); - } - } - - public static class TL_updateContactLink extends Update { - public static int constructor = 0x9d2e67c5; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - my_link = (ContactLink)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - foreign_link = (ContactLink)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - my_link.serializeToStream(stream); - foreign_link.serializeToStream(stream); - } - } - - public static class TL_updateReadMessages extends Update { - public static int constructor = 0x2e5ab668; - - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - messages.add(stream.readInt32()); - } - pts = stream.readInt32(); - pts_count = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = messages.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - stream.writeInt32(messages.get(a)); - } - stream.writeInt32(pts); - stream.writeInt32(pts_count); - } - } - - public static class TL_updateReadHistoryInbox extends Update { - public static int constructor = 0x9961fd5c; - - public Peer peer; - - public void readParams(AbsSerializedData stream) { - peer = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - max_id = stream.readInt32(); - pts = stream.readInt32(); - pts_count = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - peer.serializeToStream(stream); - stream.writeInt32(max_id); - stream.writeInt32(pts); - stream.writeInt32(pts_count); - } - } - - public static class TL_updateChatParticipantDelete extends Update { - public static int constructor = 0x6e5f8c22; - - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - user_id = stream.readInt32(); - version = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - stream.writeInt32(user_id); - stream.writeInt32(version); - } - } - - public static class TL_updateReadHistoryOutbox extends Update { - public static int constructor = 0x2f2f21bf; - - public Peer peer; - - public void readParams(AbsSerializedData stream) { - peer = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - max_id = stream.readInt32(); - pts = stream.readInt32(); - pts_count = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - peer.serializeToStream(stream); - stream.writeInt32(max_id); - stream.writeInt32(pts); - stream.writeInt32(pts_count); - } - } - - public static class TL_updateWebPage extends Update { - public static int constructor = 0x2cc36971; - - - public void readParams(AbsSerializedData stream) { - webpage = (WebPage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - webpage.serializeToStream(stream); - } - } - - public static class TL_updateServiceNotification extends Update { - public static int constructor = 0x382dd3e4; - - public String message; - - public void readParams(AbsSerializedData stream) { - type = stream.readString(); - message = stream.readString(); - media = (MessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - popup = stream.readBool(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(type); - stream.writeString(message); - media.serializeToStream(stream); - stream.writeBool(popup); - } - } - - public static class TL_updateNotifySettings extends Update { - public static int constructor = 0xbec268ef; - - - public void readParams(AbsSerializedData stream) { - peer = (NotifyPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - notify_settings = (PeerNotifySettings)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - peer.serializeToStream(stream); - notify_settings.serializeToStream(stream); - } - } - - public static class TL_updateUserTyping extends Update { - public static int constructor = 0x5c486927; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - action = (SendMessageAction)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - action.serializeToStream(stream); - } - } - - public static class TL_updateChatUserTyping extends Update { - public static int constructor = 0x9a65ea1f; - - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - user_id = stream.readInt32(); - action = (SendMessageAction) TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - stream.writeInt32(user_id); - action.serializeToStream(stream); - } - } - - public static class TL_updateUserName extends Update { - public static int constructor = 0xa7332b73; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - first_name = stream.readString(); - last_name = stream.readString(); - username = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeString(username); - } - } - - public static class TL_updateNewEncryptedMessage extends Update { - public static int constructor = 0x12bcbd9a; - - public EncryptedMessage message; - - public void readParams(AbsSerializedData stream) { - message = (EncryptedMessage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - qts = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - message.serializeToStream(stream); - stream.writeInt32(qts); - } - } - - public static class TL_updateNewMessage extends Update { - public static int constructor = 0x1f2b0afd; - - public Message message; - - public void readParams(AbsSerializedData stream) { - message = (Message)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - pts = stream.readInt32(); - pts_count = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - message.serializeToStream(stream); - stream.writeInt32(pts); - stream.writeInt32(pts_count); - } - } - - public static class TL_updateMessageID extends Update { - public static int constructor = 0x4e90bfd6; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - random_id = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeInt64(random_id); - } - } - - public static class TL_updateDeleteMessages extends Update { - public static int constructor = 0xa20db0e5; - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - messages.add(stream.readInt32()); - } - pts = stream.readInt32(); - pts_count = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = messages.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - stream.writeInt32(messages.get(a)); - } - stream.writeInt32(pts); - stream.writeInt32(pts_count); - } - } - - public static class TL_updateEncryptedChatTyping extends Update { - public static int constructor = 0x1710f156; - - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - } - } - - public static class TL_updateDcOptions extends Update { - public static int constructor = 0x8e5e9873; - - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - dc_options.add((TL_dcOption)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = dc_options.size(); - stream.writeInt32(count); - for (TL_dcOption dc_option : dc_options) { - dc_option.serializeToStream(stream); - } - } - } - - public static class TL_updateChatParticipants extends Update { - public static int constructor = 0x7761198; - - - public void readParams(AbsSerializedData stream) { - participants = (ChatParticipants)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - participants.serializeToStream(stream); - } - } - - 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; - - - public void readParams(AbsSerializedData stream) { - chat = (EncryptedChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - date = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - chat.serializeToStream(stream); - stream.writeInt32(date); - } - } - - public static class TL_updateUserBlocked extends Update { - public static int constructor = 0x80ece81a; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - blocked = stream.readBool(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - stream.writeBool(blocked); - } - } - - public static class TL_updateActivation extends Update { - public static int constructor = 0x6f690963; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - } - } - - public static class TL_updateNewAuthorization extends Update { - public static int constructor = 0x8f06529a; - - - public void readParams(AbsSerializedData stream) { - auth_key_id = stream.readInt64(); - date = stream.readInt32(); - device = stream.readString(); - location = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(auth_key_id); - stream.writeInt32(date); - stream.writeString(device); - stream.writeString(location); - } - } - - public static class TL_updateNewGeoChatMessage extends Update { - public static int constructor = 0x5a68e3f7; - - public GeoChatMessage message; - - public void readParams(AbsSerializedData stream) { - message = (GeoChatMessage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - message.serializeToStream(stream); - } - } - - public static class TL_updateUserPhoto extends Update { - public static int constructor = 0x95313b0c; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - date = stream.readInt32(); - photo = (UserProfilePhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - previous = stream.readBool(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - stream.writeInt32(date); - photo.serializeToStream(stream); - stream.writeBool(previous); - } - } - - public static class TL_updateContactRegistered extends Update { - public static int constructor = 0x2575bbb9; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - date = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - stream.writeInt32(date); - } - } - - public static class TL_updateChatParticipantAdd extends Update { - public static int constructor = 0x3a0eeb22; - - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - user_id = stream.readInt32(); - inviter_id = stream.readInt32(); - version = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - stream.writeInt32(user_id); - stream.writeInt32(inviter_id); - stream.writeInt32(version); - } - } - - public static class TL_updateUserStatus extends Update { - public static int constructor = 0x1bfbd823; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - status.serializeToStream(stream); - } - } - - public static class TL_contacts_suggested extends TLObject { - public static int constructor = 0x5649dcc5; - - public ArrayList results = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - results.add((TL_contactSuggested)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 = results.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - results.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 RpcError extends TLObject { - public int error_code; - public String error_message; - public long query_id; - } - - public static class TL_rpc_error extends RpcError { - public static int constructor = 0x2144ca19; - - - public void readParams(AbsSerializedData stream) { - error_code = stream.readInt32(); - error_message = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(error_code); - stream.writeString(error_message); - } - } - - public static class TL_rpc_req_error extends RpcError { - public static int constructor = 0x7ae432f5; - - - public void readParams(AbsSerializedData stream) { - query_id = stream.readInt64(); - error_code = stream.readInt32(); - error_message = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(query_id); - stream.writeInt32(error_code); - stream.writeString(error_message); - } - } - - public static class TL_inputEncryptedFile extends InputEncryptedFile { - public static int constructor = 0x5a17b5e5; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - } - } - - public static class TL_inputEncryptedFileBigUploaded extends InputEncryptedFile { - public static int constructor = 0x2dc173c8; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - parts = stream.readInt32(); - key_fingerprint = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt32(parts); - stream.writeInt32(key_fingerprint); - } - } - - public static class TL_inputEncryptedFileEmpty extends InputEncryptedFile { - public static int constructor = 0x1837c364; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputEncryptedFileUploaded extends InputEncryptedFile { - public static int constructor = 0x64bd0306; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - parts = stream.readInt32(); - md5_checksum = stream.readString(); - key_fingerprint = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt32(parts); - stream.writeString(md5_checksum); - stream.writeInt32(key_fingerprint); - } - } - - public static class DecryptedMessageAction extends TLObject { - public int start_seq_no; - public int end_seq_no; - public int ttl_seconds; - public int layer; - public ArrayList random_ids = new ArrayList<>(); - 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 { - public static int constructor = 0xa1733aec; - - public void readParams(AbsSerializedData stream) { - ttl_seconds = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(ttl_seconds); - } - } - - public static class TL_decryptedMessageActionFlushHistory extends DecryptedMessageAction { - public static int constructor = 0x6719e45c; - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - 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; - - - public void readParams(AbsSerializedData stream) { - start_seq_no = stream.readInt32(); - end_seq_no = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(start_seq_no); - stream.writeInt32(end_seq_no); - } - } - - public static class TL_decryptedMessageActionNotifyLayer extends DecryptedMessageAction { - public static int constructor = 0xf3048883; - - - public void readParams(AbsSerializedData stream) { - layer = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(layer); - } - } - - public static class TL_decryptedMessageActionReadMessages extends DecryptedMessageAction { - public static int constructor = 0xc4f40be; - - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - random_ids.add(stream.readInt64()); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = random_ids.size(); - stream.writeInt32(count); - for (Long random_id : random_ids) { - stream.writeInt64(random_id); - } - } - } - - 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; - - - public void readParams(AbsSerializedData stream) { - action = (SendMessageAction)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - action.serializeToStream(stream); - } - } - - public static class TL_server_DH_inner_data extends TLObject { - public static int constructor = 0xb5890dba; - - public byte[] nonce; - public byte[] server_nonce; - public int g; - public byte[] dh_prime; - public byte[] g_a; - public int server_time; - - public void readParams(AbsSerializedData stream) { - nonce = stream.readData(16); - server_nonce = stream.readData(16); - g = stream.readInt32(); - dh_prime = stream.readByteArray(); - g_a = stream.readByteArray(); - server_time = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeRaw(nonce); - stream.writeRaw(server_nonce); - stream.writeInt32(g); - stream.writeByteArray(dh_prime); - stream.writeByteArray(g_a); - stream.writeInt32(server_time); - } - } - - public static class TL_new_session_created extends TLObject { - public static int constructor = 0x9ec20908; - - public long first_msg_id; - public long unique_id; - public long server_salt; - - public void readParams(AbsSerializedData stream) { - first_msg_id = stream.readInt64(); - unique_id = stream.readInt64(); - server_salt = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(first_msg_id); - stream.writeInt64(unique_id); - stream.writeInt64(server_salt); - } - } - - public static class messages_AllStickers extends TLObject { - public String hash; - public ArrayList packs = new ArrayList<>(); - public ArrayList documents = new ArrayList<>(); - } - - public static class TL_messages_allStickers extends messages_AllStickers { - public static int constructor = 0xdcef3102; - - - public void readParams(AbsSerializedData stream) { - hash = stream.readString(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - packs.add((TL_stickerPack)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - documents.add((Document)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(hash); - stream.writeInt32(0x1cb5c415); - int count = packs.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - packs.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = documents.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - documents.get(a).serializeToStream(stream); - } - } - } - - public static class TL_messages_allStickersNotModified extends messages_AllStickers { - public static int constructor = 0xe86602c3; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class account_Password extends TLObject { - public byte[] current_salt; - public byte[] new_salt; - public String hint; - public boolean has_recovery; - public String email_unconfirmed_pattern; - } - - public static class TL_account_password extends account_Password { - public static int constructor = 0x7c18141c; - - - public void readParams(AbsSerializedData stream) { - current_salt = stream.readByteArray(); - new_salt = stream.readByteArray(); - hint = stream.readString(); - has_recovery = stream.readBool(); - email_unconfirmed_pattern = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeByteArray(current_salt); - stream.writeByteArray(new_salt); - stream.writeString(hint); - stream.writeBool(has_recovery); - stream.writeString(email_unconfirmed_pattern); - } - } - - public static class TL_account_noPassword extends account_Password { - public static int constructor = 0x96dabc18; - - - public void readParams(AbsSerializedData stream) { - new_salt = stream.readByteArray(); - email_unconfirmed_pattern = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeByteArray(new_salt); - stream.writeString(email_unconfirmed_pattern); - } - } - - public static class UserProfilePhoto extends TLObject { - public long photo_id; - public FileLocation photo_small; - public FileLocation photo_big; - } - - public static class TL_userProfilePhotoEmpty extends UserProfilePhoto { - public static int constructor = 0x4f11bae1; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_userProfilePhoto extends UserProfilePhoto { - public static int constructor = 0xd559d8c8; - - - public void readParams(AbsSerializedData stream) { - photo_id = stream.readInt64(); - photo_small = (FileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - photo_big = (FileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(photo_id); - photo_small.serializeToStream(stream); - photo_big.serializeToStream(stream); - } - } - - public static class Photo extends TLObject { + public static class InputStickerSet extends TLObject { public long id; public long access_hash; - public int user_id; - public int date; - public String caption; - public GeoPoint geo; - public ArrayList sizes = new ArrayList<>(); - } + public String short_name; - public static class TL_photo extends Photo { - public static int constructor = 0x22b56751; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - user_id = stream.readInt32(); - date = stream.readInt32(); - caption = stream.readString(); - geo = (GeoPoint)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - PhotoSize size = (PhotoSize)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - if (size != null) { - sizes.add(size); - } + public static InputStickerSet TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputStickerSet result = null; + switch(constructor) { + case 0xffb62b95: + result = new TL_inputStickerSetEmpty(); + break; + case 0x9de7a269: + result = new TL_inputStickerSetID(); + break; + case 0x861cc8a0: + result = new TL_inputStickerSetShortName(); + break; } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - stream.writeInt32(user_id); - stream.writeInt32(date); - stream.writeString(caption); - geo.serializeToStream(stream); - stream.writeInt32(0x1cb5c415); - int count = sizes.size(); - stream.writeInt32(count); - for (PhotoSize size : sizes) { - size.serializeToStream(stream); + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputStickerSet", constructor)); } - } - } - - public static class TL_photoEmpty extends Photo { - public static int constructor = 0x2331b22d; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - } - } - - public static class TL_encryptedChatWaiting extends EncryptedChat { - public static int constructor = 0x3bf703dc; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - access_hash = stream.readInt64(); - date = stream.readInt32(); - admin_id = stream.readInt32(); - participant_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeInt64(access_hash); - stream.writeInt32(date); - stream.writeInt32(admin_id); - stream.writeInt32(participant_id); - } - } - - public static class TL_encryptedChatEmpty extends EncryptedChat { - public static int constructor = 0xab7ec0a0; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - } - } - - public static class TL_encryptedChatDiscarded extends EncryptedChat { - public static int constructor = 0x13d6dd27; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - } - } - - public static class TL_encryptedChat extends EncryptedChat { - public static int constructor = 0xfa56ce36; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - access_hash = stream.readInt64(); - date = stream.readInt32(); - admin_id = stream.readInt32(); - participant_id = stream.readInt32(); - g_a_or_b = stream.readByteArray(); - key_fingerprint = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeInt64(access_hash); - stream.writeInt32(date); - stream.writeInt32(admin_id); - stream.writeInt32(participant_id); - stream.writeByteArray(g_a_or_b); - stream.writeInt64(key_fingerprint); - } - } - - public static class TL_encryptedChatRequested extends EncryptedChat { - public static int constructor = 0xc878527e; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - access_hash = stream.readInt64(); - date = stream.readInt32(); - admin_id = stream.readInt32(); - participant_id = stream.readInt32(); - g_a = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeInt64(access_hash); - stream.writeInt32(date); - stream.writeInt32(admin_id); - stream.writeInt32(participant_id); - stream.writeByteArray(g_a); - } - } - - public static class TL_geochats_statedMessage extends TLObject { - public static int constructor = 0x17b1578b; - - public GeoChatMessage message; - public ArrayList chats = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - public int seq; - - public void readParams(AbsSerializedData stream) { - message = (GeoChatMessage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - chats.add((Chat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + if (result != null) { + result.readParams(stream, exception); } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - seq = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - message.serializeToStream(stream); - stream.writeInt32(0x1cb5c415); - int count = chats.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - chats.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); - } - stream.writeInt32(seq); + return result; } } - public static class TL_contact extends TLObject { - public static int constructor = 0xf911c994; + public static class TL_inputStickerSetEmpty extends InputStickerSet { + public static int constructor = 0xffb62b95; - public int user_id; - public boolean mutual; - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - mutual = stream.readBool(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - stream.writeBool(mutual); - } - } - - public static class TL_config extends TLObject { - public static int constructor = 0x68bac247; - - public int date; - public int expires; - public boolean test_mode; - public int this_dc; - public ArrayList dc_options = new ArrayList<>(); - public int chat_size_max; - public int broadcast_size_max; - public int forwarded_count_max; - public int online_update_period_ms; - public int offline_blur_timeout_ms; - public int offline_idle_timeout_ms; - public int online_cloud_timeout_ms; - public int notify_cloud_delay_ms; - public int notify_default_delay_ms; - public int chat_big_size; - public ArrayList disabled_features = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - date = stream.readInt32(); - expires = stream.readInt32(); - test_mode = stream.readBool(); - this_dc = stream.readInt32(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - dc_options.add((TL_dcOption)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - chat_size_max = stream.readInt32(); - broadcast_size_max = stream.readInt32(); - forwarded_count_max = stream.readInt32(); - online_update_period_ms = stream.readInt32(); - offline_blur_timeout_ms = stream.readInt32(); - offline_idle_timeout_ms = stream.readInt32(); - online_cloud_timeout_ms = stream.readInt32(); - notify_cloud_delay_ms = stream.readInt32(); - notify_default_delay_ms = stream.readInt32(); - chat_big_size = stream.readInt32(); - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - disabled_features.add((TL_disabledFeature)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(date); - stream.writeInt32(expires); - stream.writeBool(test_mode); - stream.writeInt32(this_dc); - stream.writeInt32(0x1cb5c415); - int count = dc_options.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - dc_options.get(a).serializeToStream(stream); - } - stream.writeInt32(chat_size_max); - stream.writeInt32(broadcast_size_max); - stream.writeInt32(forwarded_count_max); - stream.writeInt32(online_update_period_ms); - stream.writeInt32(offline_blur_timeout_ms); - stream.writeInt32(offline_idle_timeout_ms); - stream.writeInt32(online_cloud_timeout_ms); - stream.writeInt32(notify_cloud_delay_ms); - stream.writeInt32(notify_default_delay_ms); - stream.writeInt32(chat_big_size); - stream.writeInt32(0x1cb5c415); - count = disabled_features.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - disabled_features.get(a).serializeToStream(stream); - } - } - } - - public static class TL_help_support extends TLObject { - public static int constructor = 0x17c6b5f6; - - public String phone_number; - public User user; - - public void readParams(AbsSerializedData stream) { - phone_number = stream.readString(); - user = (User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(phone_number); - user.serializeToStream(stream); - } - } - - public static class TL_help_getSupport extends TLObject { - public static int constructor = 0x9cdf08cd; - - public Class responseClass () { - return TL_help_support.class; - } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } - public static class TL_auth_sendSms extends TLObject { - public static int constructor = 0xda9f3e8; + public static class TL_inputStickerSetID extends InputStickerSet { + public static int constructor = 0x9de7a269; - public String phone_number; - public String phone_code_hash; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - phone_number = stream.readString(); - phone_code_hash = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(phone_number); - stream.writeString(phone_code_hash); - } - } - - public static class TL_messages_readMessageContents extends TLObject { - public static int constructor = 0x36a73f77; - - public ArrayList id = new ArrayList<>(); - - public Class responseClass () { - return TL_messages_affectedMessages.class; - } - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - id.add(stream.readInt32()); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = id.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - stream.writeInt32(id.get(a)); - } - } - } - - 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 rules = new ArrayList<>(); - - 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_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 TL_account_updateDeviceLocked extends TLObject { - public static int constructor = 0x38df3532; - - public int period; - - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - period = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(period); - } - } - - public static class TL_account_getAuthorizations extends TLObject { - public static int constructor = 0xe320c158; - - - public Class responseClass () { - return TL_account_authorizations.class; - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_account_resetAuthorization extends TLObject { - public static int constructor = 0xdf77f3bc; - - public long hash; - - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - hash = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(hash); - } - } - - public static class TL_messages_getAllStickers extends TLObject { - public static int constructor = 0xaa3bc868; - - public String hash; - - public Class responseClass () { - return messages_AllStickers.class; - } - - public void readParams(AbsSerializedData stream) { - hash = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(hash); - } - } - - public static class TL_messages_getWebPagePreview extends TLObject { - public static int constructor = 0x25223e24; - - public String message; - - public Class responseClass () { - return MessageMedia.class; - } - - public void readParams(AbsSerializedData stream) { - message = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(message); - } - } - - public static class TL_account_checkUsername extends TLObject { - public static int constructor = 0x2714d86c; - - public String username; - - public Class responseClass () { - return Bool.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_updateUsername extends TLObject { - public static int constructor = 0x3e0bdd7c; - - 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_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 InputAudio extends TLObject { - public long id; - public long access_hash; - } - - public static class TL_inputAudio extends InputAudio { - public static int constructor = 0x77d440ff; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); } public void serializeToStream(AbsSerializedData stream) { @@ -5653,456 +9552,64 @@ public class TLRPC { } } - public static class TL_inputAudioEmpty extends InputAudio { - public static int constructor = 0xd95adc84; + public static class TL_inputStickerSetShortName extends InputStickerSet { + public static int constructor = 0x861cc8a0; - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_messages_chats extends TLObject { - public static int constructor = 0x64ff9fd5; - - public ArrayList chats = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - chats.add((Chat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } + public void readParams(AbsSerializedData stream, boolean exception) { + short_name = stream.readString(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = chats.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - chats.get(a).serializeToStream(stream); - } - } - } - - public static class TL_contacts_found extends TLObject { - public static int constructor = 0x566000e; - - public ArrayList results = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - results.add((TL_contactFound)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 = results.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - results.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 ChatParticipants extends TLObject { - public int chat_id; - public int admin_id; - public ArrayList participants = new ArrayList<>(); - public int version; - } - - public static class TL_chatParticipants extends ChatParticipants { - public static int constructor = 0x7841b415; - - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - admin_id = stream.readInt32(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - participants.add((TL_chatParticipant)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - version = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - stream.writeInt32(admin_id); - stream.writeInt32(0x1cb5c415); - int count = participants.size(); - stream.writeInt32(count); - for (TL_chatParticipant participant : participants) { - participant.serializeToStream(stream); - } - stream.writeInt32(version); - } - } - - public static class TL_chatParticipantsForbidden extends ChatParticipants { - public static int constructor = 0xfd2bb8a; - - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - } - } - - public static class DecryptedMessageMedia extends TLObject { - public long id; - public long access_hash; - public int date; - public PhotoSize thumbImage; - public byte[] thumb; - public int thumb_w; - public int thumb_h; - public String file_name; - public String mime_type; - public int size; - public int dc_id; - public ArrayList attributes = new ArrayList<>(); - public byte[] key; - public byte[] iv; - public double lat; - public double _long; - public int duration; - public int w; - public int h; - public String phone_number; - public String first_name; - public String last_name; - public int user_id; - } - - public static class TL_decryptedMessageMediaExternalDocument extends DecryptedMessageMedia { - public static int constructor = 0xfa95b0dd; - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - date = stream.readInt32(); - mime_type = stream.readString(); - size = stream.readInt32(); - thumbImage = (PhotoSize)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - dc_id = stream.readInt32(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - attributes.add((DocumentAttribute)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - stream.writeInt32(date); - stream.writeString(mime_type); - stream.writeInt32(size); - thumbImage.serializeToStream(stream); - stream.writeInt32(dc_id); - stream.writeInt32(0x1cb5c415); - int count = attributes.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - attributes.get(a).serializeToStream(stream); - } - } - } - - public static class TL_decryptedMessageMediaDocument extends DecryptedMessageMedia { - public static int constructor = 0xb095434b; - - - public void readParams(AbsSerializedData stream) { - thumb = stream.readByteArray(); - thumb_w = stream.readInt32(); - thumb_h = stream.readInt32(); - file_name = stream.readString(); - mime_type = stream.readString(); - size = stream.readInt32(); - key = stream.readByteArray(); - iv = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeByteArray(thumb); - stream.writeInt32(thumb_w); - stream.writeInt32(thumb_h); - stream.writeString(file_name); - stream.writeString(mime_type); - stream.writeInt32(size); - stream.writeByteArray(key); - stream.writeByteArray(iv); - } - } - - public static class TL_decryptedMessageMediaGeoPoint extends DecryptedMessageMedia { - public static int constructor = 0x35480a59; - - - public void readParams(AbsSerializedData stream) { - lat = stream.readDouble(); - _long = stream.readDouble(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeDouble(lat); - stream.writeDouble(_long); - } - } - - public static class TL_decryptedMessageMediaAudio extends DecryptedMessageMedia { - public static int constructor = 0x57e0a9cb; - - - public void readParams(AbsSerializedData stream) { - duration = stream.readInt32(); - mime_type = stream.readString(); - size = stream.readInt32(); - key = stream.readByteArray(); - iv = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(duration); - stream.writeString(mime_type); - stream.writeInt32(size); - stream.writeByteArray(key); - stream.writeByteArray(iv); - } - } - - public static class TL_decryptedMessageMediaVideo extends DecryptedMessageMedia { - public static int constructor = 0x524a415d; - - - public void readParams(AbsSerializedData stream) { - thumb = stream.readByteArray(); - thumb_w = stream.readInt32(); - thumb_h = stream.readInt32(); - duration = stream.readInt32(); - mime_type = stream.readString(); - w = stream.readInt32(); - h = stream.readInt32(); - size = stream.readInt32(); - key = stream.readByteArray(); - iv = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeByteArray(thumb); - stream.writeInt32(thumb_w); - stream.writeInt32(thumb_h); - stream.writeInt32(duration); - stream.writeString(mime_type); - stream.writeInt32(w); - stream.writeInt32(h); - stream.writeInt32(size); - stream.writeByteArray(key); - stream.writeByteArray(iv); - } - } - - public static class TL_decryptedMessageMediaContact extends DecryptedMessageMedia { - public static int constructor = 0x588a0a97; - - - public void readParams(AbsSerializedData stream) { - phone_number = stream.readString(); - first_name = stream.readString(); - last_name = stream.readString(); - user_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(phone_number); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeInt32(user_id); - } - } - - public static class TL_decryptedMessageMediaEmpty extends DecryptedMessageMedia { - public static int constructor = 0x89f5c4a; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_decryptedMessageMediaPhoto extends DecryptedMessageMedia { - public static int constructor = 0x32798a8c; - - - public void readParams(AbsSerializedData stream) { - thumb = stream.readByteArray(); - thumb_w = stream.readInt32(); - thumb_h = stream.readInt32(); - w = stream.readInt32(); - h = stream.readInt32(); - size = stream.readInt32(); - key = stream.readByteArray(); - iv = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeByteArray(thumb); - stream.writeInt32(thumb_w); - stream.writeInt32(thumb_h); - stream.writeInt32(w); - stream.writeInt32(h); - stream.writeInt32(size); - stream.writeByteArray(key); - stream.writeByteArray(iv); - } - } - - public static class TL_chatParticipant extends TLObject { - public static int constructor = 0xc8d7493e; - - public int user_id; - public int inviter_id; - public int date; - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - inviter_id = stream.readInt32(); - date = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - stream.writeInt32(inviter_id); - stream.writeInt32(date); - } - } - - public static class Chat extends TLObject { - public int id; - public String title; - public int date; - public long access_hash; - public String address; - public String venue; - public GeoPoint geo; - public ChatPhoto photo; - public int participants_count; - public boolean checked_in; - public int version; - public boolean left; - } - - public static class TL_chatForbidden extends Chat { - public static int constructor = 0xfb0ccc41; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - title = stream.readString(); - date = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(title); - stream.writeInt32(date); - } - } - - public static class TL_geoChat extends Chat { - public static int constructor = 0x75eaea5a; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - access_hash = stream.readInt64(); - title = stream.readString(); - address = stream.readString(); - venue = stream.readString(); - geo = (GeoPoint)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - photo = (ChatPhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - participants_count = stream.readInt32(); - date = stream.readInt32(); - checked_in = stream.readBool(); - version = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeInt64(access_hash); - stream.writeString(title); - stream.writeString(address); - stream.writeString(venue); - geo.serializeToStream(stream); - photo.serializeToStream(stream); - stream.writeInt32(participants_count); - stream.writeInt32(date); - stream.writeBool(checked_in); - stream.writeInt32(version); - } - } - public static class TL_chat extends Chat { - public static int constructor = 0x6e9c9bc7; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - title = stream.readString(); - photo = (ChatPhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - participants_count = stream.readInt32(); - date = stream.readInt32(); - left = stream.readBool(); - version = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(title); - photo.serializeToStream(stream); - stream.writeInt32(participants_count); - stream.writeInt32(date); - stream.writeBool(left); - stream.writeInt32(version); + stream.writeString(short_name); } } public static class storage_FileType extends TLObject { + + public static storage_FileType TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + storage_FileType result = null; + switch(constructor) { + case 0xaa963b05: + result = new TL_storage_fileUnknown(); + break; + case 0xb3cea0e4: + result = new TL_storage_fileMp4(); + break; + case 0x1081464c: + result = new TL_storage_fileWebp(); + break; + case 0xa4f63c0: + result = new TL_storage_filePng(); + break; + case 0xcae1aadf: + result = new TL_storage_fileGif(); + break; + case 0xae1e508d: + result = new TL_storage_filePdf(); + break; + case 0x528a0677: + result = new TL_storage_fileMp3(); + break; + case 0x7efe0e: + result = new TL_storage_fileJpeg(); + break; + case 0x4b09ebbc: + result = new TL_storage_fileMov(); + break; + case 0x40bc6f52: + result = new TL_storage_filePartial(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in storage_FileType", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } } public static class TL_storage_fileUnknown extends storage_FileType { @@ -6114,6 +9621,15 @@ public class TLRPC { } } + public static class TL_storage_fileMp4 extends storage_FileType { + public static int constructor = 0xb3cea0e4; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + public static class TL_storage_fileWebp extends storage_FileType { public static int constructor = 0x1081464c; @@ -6150,15 +9666,6 @@ public class TLRPC { } } - public static class TL_storage_fileMov extends storage_FileType { - public static int constructor = 0x4b09ebbc; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - public static class TL_storage_fileMp3 extends storage_FileType { public static int constructor = 0x528a0677; @@ -6177,6 +9684,15 @@ public class TLRPC { } } + public static class TL_storage_fileMov extends storage_FileType { + public static int constructor = 0x4b09ebbc; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + public static class TL_storage_filePartial extends storage_FileType { public static int constructor = 0x40bc6f52; @@ -6186,522 +9702,267 @@ public class TLRPC { } } - public static class TL_storage_fileMp4 extends storage_FileType { - public static int constructor = 0xb3cea0e4; + public static class TL_account_authorizations extends TLObject { + public static int constructor = 0x1250abde; + public ArrayList authorizations = new ArrayList<>(); - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class MessagesFilter extends TLObject { - } - - public static class TL_inputMessagesFilterVideo extends MessagesFilter { - public static int constructor = 0x9fc00e65; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputMessagesFilterEmpty extends MessagesFilter { - public static int constructor = 0x57e2f66c; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputMessagesFilterPhotos extends MessagesFilter { - public static int constructor = 0x9609a51c; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputMessagesFilterPhotoVideo extends MessagesFilter { - public static int constructor = 0x56e9f0e4; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputMessagesFilterDocument extends MessagesFilter { - public static int constructor = 0x9eddf188; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputMessagesFilterAudio extends MessagesFilter { - public static int constructor = 0xcfc87522; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_msgs_state_info extends TLObject { - public static int constructor = 0x04deb57d; - - public long req_msg_id; - public String info; - - public void readParams(AbsSerializedData stream) { - req_msg_id = stream.readInt64(); - info = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(req_msg_id); - stream.writeString(info); - } - } - - public static class TL_fileLocation extends FileLocation { - public static int constructor = 0x53d69076; - - - public void readParams(AbsSerializedData stream) { - dc_id = stream.readInt32(); - volume_id = stream.readInt64(); - local_id = stream.readInt32(); - secret = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(dc_id); - stream.writeInt64(volume_id); - stream.writeInt32(local_id); - stream.writeInt64(secret); - } - } - - public static class TL_fileLocationUnavailable extends FileLocation { - public static int constructor = 0x7c596b46; - - - public void readParams(AbsSerializedData stream) { - volume_id = stream.readInt64(); - local_id = stream.readInt32(); - secret = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(volume_id); - stream.writeInt32(local_id); - stream.writeInt64(secret); - } - } - - public static class messages_Message extends TLObject { - public Message message; - public ArrayList chats = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - } - - public static class TL_messages_messageEmpty extends messages_Message { - public static int constructor = 0x3f4e0648; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_messages_message extends messages_Message { - public static int constructor = 0xff90c417; - - - public void readParams(AbsSerializedData stream) { - message = (Message)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - chats.add((Chat)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 static TL_account_authorizations TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_account_authorizations.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_account_authorizations", constructor)); + } else { + return null; + } } + TL_account_authorizations result = new TL_account_authorizations(); + result.readParams(stream, exception); + return result; } - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - message.serializeToStream(stream); - stream.writeInt32(0x1cb5c415); - int count = chats.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - chats.get(a).serializeToStream(stream); + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; } - stream.writeInt32(0x1cb5c415); - count = users.size(); - stream.writeInt32(count); + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - users.get(a).serializeToStream(stream); - } - } - } - - public static class TL_geochats_located extends TLObject { - public static int constructor = 0x48feb267; - - public ArrayList results = new ArrayList<>(); - public ArrayList messages = new ArrayList<>(); - public ArrayList chats = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - results.add((TL_chatLocated)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - messages.add((GeoChatMessage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - chats.add((Chat)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())); + TL_authorization object = TL_authorization.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + authorizations.add(object); } } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); - int count = results.size(); + int count = authorizations.size(); stream.writeInt32(count); for (int a = 0; a < count; a++) { - results.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = messages.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - messages.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = chats.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - chats.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); + authorizations.get(a).serializeToStream(stream); } } } - public static class TL_inputGeoChat extends TLObject { - public static int constructor = 0x74d456fa; + public static class TL_p_q_inner_data extends TLObject { + public static int constructor = 0x83c95aec; - public int chat_id; + public byte[] pq; + public byte[] p; + public byte[] q; + public byte[] nonce; + public byte[] server_nonce; + public byte[] new_nonce; + + public static TL_p_q_inner_data TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_p_q_inner_data.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_p_q_inner_data", constructor)); + } else { + return null; + } + } + TL_p_q_inner_data result = new TL_p_q_inner_data(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + pq = stream.readByteArray(exception); + p = stream.readByteArray(exception); + q = stream.readByteArray(exception); + nonce = stream.readData(16, exception); + server_nonce = stream.readData(16, exception); + new_nonce = stream.readData(32, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeByteArray(pq); + stream.writeByteArray(p); + stream.writeByteArray(q); + stream.writeRaw(nonce); + stream.writeRaw(server_nonce); + stream.writeRaw(new_nonce); + } + } + + public static class InputPhoto extends TLObject { + public long id; public long access_hash; - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - access_hash = stream.readInt64(); + public static InputPhoto TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputPhoto result = null; + switch(constructor) { + case 0x1cd7bf0d: + result = new TL_inputPhotoEmpty(); + break; + case 0xfb95c6c4: + result = new TL_inputPhoto(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputPhoto", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inputPhotoEmpty extends InputPhoto { + public static int constructor = 0x1cd7bf0d; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputPhoto extends InputPhoto { + public static int constructor = 0xfb95c6c4; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(chat_id); + stream.writeInt64(id); stream.writeInt64(access_hash); } } - public static class TL_protoMessage extends TLObject { - public static int constructor = 0x5bb8e511; + public static class contacts_Contacts extends TLObject { + public ArrayList contacts = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); - public long msg_id; - public int seqno; - public int bytes; - public TLObject body; + public static contacts_Contacts TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + contacts_Contacts result = null; + switch(constructor) { + case 0xb74ba9d2: + result = new TL_contacts_contactsNotModified(); + break; + case 0x6f8b8cb2: + result = new TL_contacts_contacts(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in contacts_Contacts", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } - public void readParams(AbsSerializedData stream) { - msg_id = stream.readInt64(); - seqno = stream.readInt32(); - bytes = stream.readInt32(); - body = TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public static class TL_contacts_contactsNotModified extends contacts_Contacts { + public static int constructor = 0xb74ba9d2; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_contacts_contacts extends contacts_Contacts { + public static int constructor = 0x6f8b8cb2; + + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_contact object = TL_contact.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + contacts.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt64(msg_id); - stream.writeInt32(seqno); - stream.writeInt32(bytes); - body.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = contacts.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + contacts.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 PhotoSize extends TLObject { - public String type; - public FileLocation location; - public int w; - public int h; - public int size; - public byte[] bytes; - } - - public static class TL_photoSize extends PhotoSize { - public static int constructor = 0x77bfb61b; - - - public void readParams(AbsSerializedData stream) { - type = stream.readString(); - location = (FileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - w = stream.readInt32(); - h = stream.readInt32(); - size = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(type); - location.serializeToStream(stream); - stream.writeInt32(w); - stream.writeInt32(h); - stream.writeInt32(size); - } - } - - public static class TL_photoSizeEmpty extends PhotoSize { - public static int constructor = 0xe17e23c; - - - public void readParams(AbsSerializedData stream) { - type = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(type); - } - } - - public static class TL_photoCachedSize extends PhotoSize { - public static int constructor = 0xe9a734fa; - - - public void readParams(AbsSerializedData stream) { - type = stream.readString(); - location = (FileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - w = stream.readInt32(); - h = stream.readInt32(); - bytes = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(type); - location.serializeToStream(stream); - stream.writeInt32(w); - stream.writeInt32(h); - stream.writeByteArray(bytes); - } - } - - public static class TL_contactFound extends TLObject { - public static int constructor = 0xea879f95; + public static class TL_contactBlocked extends TLObject { + public static int constructor = 0x561bc879; public int user_id; + public int date; - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); + public static TL_contactBlocked TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_contactBlocked.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_contactBlocked", constructor)); + } else { + return null; + } + } + TL_contactBlocked result = new TL_contactBlocked(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(user_id); - } - } - - 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; - public String name; - public String md5_checksum; - } - - public static class TL_inputFileBig extends InputFile { - public static int constructor = 0xfa4f0bb5; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - parts = stream.readInt32(); - name = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt32(parts); - stream.writeString(name); - } - } - - public static class TL_inputFile extends InputFile { - public static int constructor = 0xf52ff27f; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - parts = stream.readInt32(); - name = stream.readString(); - md5_checksum = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt32(parts); - stream.writeString(name); - stream.writeString(md5_checksum); - } - } - - public static class TL_userFull extends TLObject { - public static int constructor = 0x771095da; - - public User user; - public TL_contacts_link link; - public Photo profile_photo; - public PeerNotifySettings notify_settings; - public boolean blocked; - public String real_first_name; - public String real_last_name; - - public void readParams(AbsSerializedData stream) { - user = (User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - link = (TL_contacts_link)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - profile_photo = (Photo)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - notify_settings = (PeerNotifySettings)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - blocked = stream.readBool(); - real_first_name = stream.readString(); - real_last_name = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - user.serializeToStream(stream); - link.serializeToStream(stream); - profile_photo.serializeToStream(stream); - notify_settings.serializeToStream(stream); - stream.writeBool(blocked); - stream.writeString(real_first_name); - stream.writeString(real_last_name); - } - } - - public static class TL_updates_state extends TLObject { - public static int constructor = 0xa56c2a3e; - - public int pts; - public int qts; - public int date; - public int seq; - public int unread_count; - - public void readParams(AbsSerializedData stream) { - pts = stream.readInt32(); - qts = stream.readInt32(); - date = stream.readInt32(); - seq = stream.readInt32(); - unread_count = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(pts); - stream.writeInt32(qts); stream.writeInt32(date); - stream.writeInt32(seq); - stream.writeInt32(unread_count); - } - } - - public static class TL_resPQ extends TLObject { - public static int constructor = 0x05162463; - - public byte[] nonce; - public byte[] server_nonce; - public byte[] pq; - public ArrayList server_public_key_fingerprints = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - nonce = stream.readData(16); - server_nonce = stream.readData(16); - pq = stream.readByteArray(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - server_public_key_fingerprints.add(stream.readInt64()); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeRaw(nonce); - stream.writeRaw(server_nonce); - stream.writeByteArray(pq); - stream.writeInt32(0x1cb5c415); - int count = server_public_key_fingerprints.size(); - stream.writeInt32(count); - for (Long server_public_key_fingerprint : server_public_key_fingerprints) { - stream.writeInt64(server_public_key_fingerprint); - } } } public static class Updates extends TLObject { public int flags; public int id; + public int from_id; public int chat_id; public String message; public int pts; @@ -6717,28 +9978,60 @@ public class TLRPC { public int user_id; public Update update; public int seq_start; - public int qts; + + public static Updates TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + Updates result = null; + switch(constructor) { + case 0x52238b3c: + result = new TL_updateShortChatMessage(); + break; + case 0x74ae4240: + result = new TL_updates(); + break; + case 0xed5c2127: + result = new TL_updateShortMessage(); + break; + case 0x78d4dec1: + result = new TL_updateShort(); + break; + case 0x725b04c3: + result = new TL_updatesCombined(); + break; + case 0xe317af7e: + result = new TL_updatesTooLong(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in Updates", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } } public static class TL_updateShortChatMessage extends Updates { public static int constructor = 0x52238b3c; - public void readParams(AbsSerializedData stream) { - flags = stream.readInt32(); - id = stream.readInt32(); - user_id = stream.readInt32(); - chat_id = stream.readInt32(); - message = stream.readString(); - pts = stream.readInt32(); - pts_count = stream.readInt32(); - date = stream.readInt32(); - if ((flags & MESSAGE_FLAG_FWD) != 0) { - fwd_from_id = stream.readInt32(); - fwd_date = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + id = stream.readInt32(exception); + from_id = stream.readInt32(exception); + chat_id = stream.readInt32(exception); + message = stream.readString(exception); + pts = stream.readInt32(exception); + pts_count = stream.readInt32(exception); + date = stream.readInt32(exception); + if ((flags & 4) != 0) { + fwd_from_id = stream.readInt32(exception); } - if ((flags & MESSAGE_FLAG_REPLY) != 0) { - reply_to_msg_id = stream.readInt32(); + if ((flags & 4) != 0) { + fwd_date = stream.readInt32(exception); + } + if ((flags & 8) != 0) { + reply_to_msg_id = stream.readInt32(exception); } } @@ -6746,17 +10039,19 @@ public class TLRPC { stream.writeInt32(constructor); stream.writeInt32(flags); stream.writeInt32(id); - stream.writeInt32(user_id); + stream.writeInt32(from_id); stream.writeInt32(chat_id); stream.writeString(message); stream.writeInt32(pts); stream.writeInt32(pts_count); stream.writeInt32(date); - if ((flags & MESSAGE_FLAG_FWD) != 0) { + if ((flags & 4) != 0) { stream.writeInt32(fwd_from_id); + } + if ((flags & 4) != 0) { stream.writeInt32(fwd_date); } - if ((flags & MESSAGE_FLAG_REPLY) != 0) { + if ((flags & 8) != 0) { stream.writeInt32(reply_to_msg_id); } } @@ -6766,24 +10061,54 @@ public class TLRPC { public static int constructor = 0x74ae4240; - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - updates.add((Update)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; } - stream.readInt32(); - count = stream.readInt32(); + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + Update object = Update.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + updates.add(object); } - stream.readInt32(); - count = stream.readInt32(); + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - chats.add((Chat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); } - date = stream.readInt32(); - seq = stream.readInt32(); + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Chat object = Chat.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + chats.add(object); + } + date = stream.readInt32(exception); + seq = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { @@ -6815,20 +10140,22 @@ public class TLRPC { public static int constructor = 0xed5c2127; - public void readParams(AbsSerializedData stream) { - flags = stream.readInt32(); - id = stream.readInt32(); - user_id = stream.readInt32(); - message = stream.readString(); - pts = stream.readInt32(); - pts_count = stream.readInt32(); - date = stream.readInt32(); - if ((flags & MESSAGE_FLAG_FWD) != 0) { - fwd_from_id = stream.readInt32(); - fwd_date = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + id = stream.readInt32(exception); + user_id = stream.readInt32(exception); + message = stream.readString(exception); + pts = stream.readInt32(exception); + pts_count = stream.readInt32(exception); + date = stream.readInt32(exception); + if ((flags & 4) != 0) { + fwd_from_id = stream.readInt32(exception); } - if ((flags & MESSAGE_FLAG_REPLY) != 0) { - reply_to_msg_id = stream.readInt32(); + if ((flags & 4) != 0) { + fwd_date = stream.readInt32(exception); + } + if ((flags & 8) != 0) { + reply_to_msg_id = stream.readInt32(exception); } } @@ -6841,11 +10168,13 @@ public class TLRPC { stream.writeInt32(pts); stream.writeInt32(pts_count); stream.writeInt32(date); - if ((flags & MESSAGE_FLAG_FWD) != 0) { + if ((flags & 4) != 0) { stream.writeInt32(fwd_from_id); + } + if ((flags & 4) != 0) { stream.writeInt32(fwd_date); } - if ((flags & MESSAGE_FLAG_REPLY) != 0) { + if ((flags & 8) != 0) { stream.writeInt32(reply_to_msg_id); } } @@ -6855,9 +10184,9 @@ public class TLRPC { public static int constructor = 0x78d4dec1; - public void readParams(AbsSerializedData stream) { - update = (Update)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - date = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + update = Update.TLdeserialize(stream, stream.readInt32(exception), exception); + date = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { @@ -6871,25 +10200,55 @@ public class TLRPC { public static int constructor = 0x725b04c3; - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - updates.add((Update)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; } - stream.readInt32(); - count = stream.readInt32(); + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + Update object = Update.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + updates.add(object); } - stream.readInt32(); - count = stream.readInt32(); + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - chats.add((Chat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); } - date = stream.readInt32(); - seq_start = stream.readInt32(); - seq = stream.readInt32(); + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Chat object = Chat.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + chats.add(object); + } + date = stream.readInt32(exception); + seq_start = stream.readInt32(exception); + seq = stream.readInt32(exception); } public void serializeToStream(AbsSerializedData stream) { @@ -6927,438 +10286,411 @@ public class TLRPC { } } - public static class WallPaper extends TLObject { - public int id; - public String title; - public ArrayList sizes = new ArrayList<>(); - public int color; - public int bg_color; + public static class InputPeerNotifyEvents extends TLObject { + + public static InputPeerNotifyEvents TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputPeerNotifyEvents result = null; + switch(constructor) { + case 0xe86a2c74: + result = new TL_inputPeerNotifyEventsAll(); + break; + case 0xf03064d8: + result = new TL_inputPeerNotifyEventsEmpty(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputPeerNotifyEvents", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } } - public static class TL_wallPaper extends WallPaper { - public static int constructor = 0xccb03657; + public static class TL_inputPeerNotifyEventsAll extends InputPeerNotifyEvents { + public static int constructor = 0xe86a2c74; - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - title = stream.readString(); - stream.readInt32(); - int count = stream.readInt32(); + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputPeerNotifyEventsEmpty extends InputPeerNotifyEvents { + public static int constructor = 0xf03064d8; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_msgs_state_info extends TLObject { + public static int constructor = 0x04deb57d; + + public long req_msg_id; + public String info; + + public static TL_msgs_state_info TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_msgs_state_info.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_msgs_state_info", constructor)); + } else { + return null; + } + } + TL_msgs_state_info result = new TL_msgs_state_info(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + req_msg_id = stream.readInt64(exception); + info = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(req_msg_id); + stream.writeString(info); + } + } + + public static class updates_Difference extends TLObject { + public int date; + public int seq; + public ArrayList new_messages = new ArrayList<>(); + public ArrayList new_encrypted_messages = new ArrayList<>(); + public ArrayList other_updates = new ArrayList<>(); + public ArrayList chats = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + public TL_updates_state intermediate_state; + public TL_updates_state state; + + public static updates_Difference TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + updates_Difference result = null; + switch(constructor) { + case 0x5d75a138: + result = new TL_updates_differenceEmpty(); + break; + case 0xa8fb1981: + result = new TL_updates_differenceSlice(); + break; + case 0xf49ca0: + result = new TL_updates_difference(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in updates_Difference", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_updates_differenceEmpty extends updates_Difference { + public static int constructor = 0x5d75a138; + + + public void readParams(AbsSerializedData stream, boolean exception) { + date = stream.readInt32(exception); + seq = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(date); + stream.writeInt32(seq); + } + } + + public static class TL_updates_differenceSlice extends updates_Difference { + public static int constructor = 0xa8fb1981; + + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - sizes.add((PhotoSize)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + Message object = Message.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + new_messages.add(object); } - color = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(title); - stream.writeInt32(0x1cb5c415); - int count = sizes.size(); - stream.writeInt32(count); - for (PhotoSize size : sizes) { - size.serializeToStream(stream); + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; } - stream.writeInt32(color); - } - } - - public static class TL_wallPaperSolid extends WallPaper { - public static int constructor = 0x63117f24; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - title = stream.readString(); - bg_color = stream.readInt32(); - color = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(title); - stream.writeInt32(bg_color); - stream.writeInt32(color); - } - } - - public static class MsgDetailedInfo extends TLObject { - public long answer_msg_id; - public int bytes; - public int status; - public long msg_id; - } - - public static class TL_msg_new_detailed_info extends MsgDetailedInfo { - public static int constructor = 0x809db6df; - - - public void readParams(AbsSerializedData stream) { - answer_msg_id = stream.readInt64(); - bytes = stream.readInt32(); - status = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(answer_msg_id); - stream.writeInt32(bytes); - stream.writeInt32(status); - } - } - - public static class TL_msg_detailed_info extends MsgDetailedInfo { - public static int constructor = 0x276d3ec6; - - - public void readParams(AbsSerializedData stream) { - msg_id = stream.readInt64(); - answer_msg_id = stream.readInt64(); - bytes = stream.readInt32(); - status = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(msg_id); - stream.writeInt64(answer_msg_id); - stream.writeInt32(bytes); - stream.writeInt32(status); - } - } - - public static class TL_stickerPack extends TLObject { - public static int constructor = 0x12b299d4; - - public String emoticon; - public ArrayList documents = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - emoticon = stream.readString(); - stream.readInt32(); - int count = stream.readInt32(); + count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - documents.add(stream.readInt64()); + EncryptedMessage object = EncryptedMessage.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + new_encrypted_messages.add(object); } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Update object = Update.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + other_updates.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Chat object = Chat.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + chats.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + intermediate_state = TL_updates_state.TLdeserialize(stream, stream.readInt32(exception), exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeString(emoticon); stream.writeInt32(0x1cb5c415); - int count = documents.size(); + int count = new_messages.size(); stream.writeInt32(count); for (int a = 0; a < count; a++) { - stream.writeInt64(documents.get(a)); + new_messages.get(a).serializeToStream(stream); } - } - } - - public static class TL_inputEncryptedChat extends TLObject { - public static int constructor = 0xf141b5e1; - - public int chat_id; - public long access_hash; - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - access_hash = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - stream.writeInt64(access_hash); - } - } - - public static class InputChatPhoto extends TLObject { - public InputPhoto id; - public InputPhotoCrop crop; - public InputFile file; - } - - public static class TL_inputChatPhoto extends InputChatPhoto { - public static int constructor = 0xb2e1bf08; - - - public void readParams(AbsSerializedData stream) { - id = (InputPhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - crop = (InputPhotoCrop)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - id.serializeToStream(stream); - crop.serializeToStream(stream); - } - } - - public static class TL_inputChatPhotoEmpty extends InputChatPhoto { - public static int constructor = 0x1ca48f57; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputChatUploadedPhoto extends InputChatPhoto { - public static int constructor = 0x94254732; - - - public void readParams(AbsSerializedData stream) { - file = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - crop = (InputPhotoCrop)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - file.serializeToStream(stream); - crop.serializeToStream(stream); - } - } - - public static class InputVideo extends TLObject { - public long id; - public long access_hash; - } - - public static class TL_inputVideoEmpty extends InputVideo { - public static int constructor = 0x5508ec75; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputVideo extends InputVideo { - public static int constructor = 0xee579652; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - } - } - - public static class TL_nearestDc extends TLObject { - public static int constructor = 0x8e1a1775; - - public String country; - public int this_dc; - public int nearest_dc; - - public void readParams(AbsSerializedData stream) { - country = stream.readString(); - this_dc = stream.readInt32(); - nearest_dc = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(country); - stream.writeInt32(this_dc); - stream.writeInt32(nearest_dc); - } - } - - public static class InputPhoto extends TLObject { - public long id; - public long access_hash; - } - - public static class TL_inputPhotoEmpty extends InputPhoto { - public static int constructor = 0x1cd7bf0d; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputPhoto extends InputPhoto { - public static int constructor = 0xfb95c6c4; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - } - } - - public static class TL_importedContact extends TLObject { - public static int constructor = 0xd0028438; - - public int user_id; - public long client_id; - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - client_id = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - stream.writeInt64(client_id); - } - } - - 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; - public long access_hash; - } - - public static class TL_inputPeerContact extends InputPeer { - public static int constructor = 0x1023dbe8; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - } - } - - public static class TL_inputPeerChat extends InputPeer { - public static int constructor = 0x179be863; - - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(chat_id); - } - } - - public static class TL_inputPeerEmpty extends InputPeer { - public static int constructor = 0x7f3b18ea; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputPeerSelf extends InputPeer { - public static int constructor = 0x7da07ec9; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_inputPeerForeign extends InputPeer { - public static int constructor = 0x9b447325; - - - public void readParams(AbsSerializedData stream) { - user_id = stream.readInt32(); - access_hash = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(user_id); - stream.writeInt64(access_hash); - } - } - - public static class TL_account_passwordInputSettings extends TLObject { - public static int constructor = 0xbcfc532c; - - public int flags; - public byte[] new_salt; - public byte[] new_password_hash; - public String hint; - public String email; - - public void readParams(AbsSerializedData stream) { - flags = stream.readInt32(); - if ((flags & 1) != 0) { - new_salt = stream.readByteArray(); - new_password_hash = stream.readByteArray(); - hint = stream.readString(); + stream.writeInt32(0x1cb5c415); + count = new_encrypted_messages.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + new_encrypted_messages.get(a).serializeToStream(stream); } - if ((flags & 2) != 0) { - email = stream.readString(); + stream.writeInt32(0x1cb5c415); + count = other_updates.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + other_updates.get(a).serializeToStream(stream); } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(flags); - if ((flags & 1) != 0) { - stream.writeByteArray(new_salt); - stream.writeByteArray(new_password_hash); - stream.writeString(hint); + stream.writeInt32(0x1cb5c415); + count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.get(a).serializeToStream(stream); } - if ((flags & 2) != 0) { - stream.writeString(email); + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); } + intermediate_state.serializeToStream(stream); } } - public static class TL_dcOption extends TLObject { - public static int constructor = 0x2ec2a43c; + public static class TL_updates_difference extends updates_Difference { + public static int constructor = 0xf49ca0; - public int id; - public String hostname; - public String ip_address; - public int port; - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - hostname = stream.readString(); - ip_address = stream.readString(); - port = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Message object = Message.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + new_messages.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + EncryptedMessage object = EncryptedMessage.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + new_encrypted_messages.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Update object = Update.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + other_updates.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Chat object = Chat.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + chats.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + state = TL_updates_state.TLdeserialize(stream, stream.readInt32(exception), exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(hostname); - stream.writeString(ip_address); - stream.writeInt32(port); + stream.writeInt32(0x1cb5c415); + int count = new_messages.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + new_messages.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = new_encrypted_messages.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + new_encrypted_messages.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = other_updates.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + other_updates.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.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); + } + state.serializeToStream(stream); + } + } + + public static class ChatPhoto extends TLObject { + public FileLocation photo_small; + public FileLocation photo_big; + + public static ChatPhoto TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + ChatPhoto result = null; + switch(constructor) { + case 0x37c1011c: + result = new TL_chatPhotoEmpty(); + break; + case 0x6153276a: + result = new TL_chatPhoto(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in ChatPhoto", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_chatPhotoEmpty extends ChatPhoto { + public static int constructor = 0x37c1011c; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_chatPhoto extends ChatPhoto { + public static int constructor = 0x6153276a; + + + public void readParams(AbsSerializedData stream, boolean exception) { + photo_small = FileLocation.TLdeserialize(stream, stream.readInt32(exception), exception); + photo_big = FileLocation.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + photo_small.serializeToStream(stream); + photo_big.serializeToStream(stream); } } @@ -7371,12 +10703,25 @@ public class TLRPC { public int out_seq_no; public DecryptedMessage message; - public void readParams(AbsSerializedData stream) { - random_bytes = stream.readByteArray(); - layer = stream.readInt32(); - in_seq_no = stream.readInt32(); - out_seq_no = stream.readInt32(); - message = (DecryptedMessage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public static TL_decryptedMessageLayer TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_decryptedMessageLayer.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_decryptedMessageLayer", constructor)); + } else { + return null; + } + } + TL_decryptedMessageLayer result = new TL_decryptedMessageLayer(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + random_bytes = stream.readByteArray(exception); + layer = stream.readInt32(exception); + in_seq_no = stream.readInt32(exception); + out_seq_no = stream.readInt32(exception); + message = DecryptedMessage.TLdeserialize(stream, stream.readInt32(exception), exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7389,10 +10734,661 @@ public class TLRPC { } } + public static class Audio extends TLObject { + public long id; + public long access_hash; + public int user_id; + public int date; + public int duration; + public int size; + public int dc_id; + public String mime_type; + public byte[] key; + public byte[] iv; + + public static Audio TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + Audio result = null; + switch(constructor) { + case 0x427425e7: + result = new TL_audio_old(); + break; + case 0xc7ac6496: + result = new TL_audio(); + break; + case 0x555555F6: + result = new TL_audioEncrypted(); + break; + case 0x586988d8: + result = new TL_audioEmpty(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in Audio", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_audio_old extends TL_audio { + public static int constructor = 0x427425e7; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); + duration = stream.readInt32(exception); + size = stream.readInt32(exception); + dc_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(user_id); + stream.writeInt32(date); + stream.writeInt32(duration); + stream.writeInt32(size); + stream.writeInt32(dc_id); + } + } + + public static class TL_audio extends Audio { + public static int constructor = 0xc7ac6496; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); + duration = stream.readInt32(exception); + mime_type = stream.readString(exception); + size = stream.readInt32(exception); + dc_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(user_id); + stream.writeInt32(date); + stream.writeInt32(duration); + stream.writeString(mime_type); + stream.writeInt32(size); + stream.writeInt32(dc_id); + } + } + + public static class TL_audioEncrypted extends TL_audio { + public static int constructor = 0x555555F6; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); + duration = stream.readInt32(exception); + size = stream.readInt32(exception); + dc_id = stream.readInt32(exception); + key = stream.readByteArray(exception); + iv = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(user_id); + stream.writeInt32(date); + stream.writeInt32(duration); + stream.writeInt32(size); + stream.writeInt32(dc_id); + stream.writeByteArray(key); + stream.writeByteArray(iv); + } + } + + public static class TL_audioEmpty extends Audio { + public static int constructor = 0x586988d8; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + } + } + + public static class TL_contacts_found extends TLObject { + public static int constructor = 0x566000e; + + public ArrayList results = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + + public static TL_contacts_found TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_contacts_found.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_contacts_found", constructor)); + } else { + return null; + } + } + TL_contacts_found result = new TL_contacts_found(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_contactFound object = TL_contactFound.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + results.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = results.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + results.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 Document extends TLObject { + public long id; + public long access_hash; + public int user_id; + public int date; + public String file_name; + public String mime_type; + public int size; + public PhotoSize thumb; + public int dc_id; + public byte[] key; + public byte[] iv; + public ArrayList attributes = new ArrayList<>(); + + public static Document TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + Document result = null; + switch(constructor) { + case 0x55555556: + result = new TL_documentEncrypted_old(); + break; + case 0x9efc6326: + result = new TL_document_old(); + break; + case 0x36f8c871: + result = new TL_documentEmpty(); + break; + case 0x55555558: + result = new TL_documentEncrypted(); + break; + case 0xf9a39f4f: + result = new TL_document(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in Document", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_documentEncrypted_old extends TL_document { + public static int constructor = 0x55555556; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); + file_name = stream.readString(exception); + mime_type = stream.readString(exception); + size = stream.readInt32(exception); + thumb = PhotoSize.TLdeserialize(stream, stream.readInt32(exception), exception); + dc_id = stream.readInt32(exception); + key = stream.readByteArray(exception); + iv = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(user_id); + stream.writeInt32(date); + stream.writeString(file_name); + stream.writeString(mime_type); + stream.writeInt32(size); + thumb.serializeToStream(stream); + stream.writeInt32(dc_id); + stream.writeByteArray(key); + stream.writeByteArray(iv); + } + } + + public static class TL_document_old extends TL_document { + public static int constructor = 0x9efc6326; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + user_id = stream.readInt32(exception); + date = stream.readInt32(exception); + file_name = stream.readString(exception); + mime_type = stream.readString(exception); + size = stream.readInt32(exception); + thumb = PhotoSize.TLdeserialize(stream, stream.readInt32(exception), exception); + dc_id = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(user_id); + stream.writeInt32(date); + stream.writeString(file_name); + stream.writeString(mime_type); + stream.writeInt32(size); + thumb.serializeToStream(stream); + stream.writeInt32(dc_id); + } + } + + public static class TL_documentEmpty extends Document { + public static int constructor = 0x36f8c871; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + } + } + + public static class TL_documentEncrypted extends Document { + public static int constructor = 0x55555558; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + date = stream.readInt32(exception); + mime_type = stream.readString(exception); + size = stream.readInt32(exception); + thumb = PhotoSize.TLdeserialize(stream, stream.readInt32(exception), exception); + dc_id = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + DocumentAttribute object = DocumentAttribute.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + attributes.add(object); + } + key = stream.readByteArray(exception); + iv = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(date); + stream.writeString(mime_type); + stream.writeInt32(size); + thumb.serializeToStream(stream); + stream.writeInt32(dc_id); + stream.writeInt32(0x1cb5c415); + int count = attributes.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + attributes.get(a).serializeToStream(stream); + } + stream.writeByteArray(key); + stream.writeByteArray(iv); + } + } + + public static class TL_document extends Document { + public static int constructor = 0xf9a39f4f; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + date = stream.readInt32(exception); + mime_type = stream.readString(exception); + size = stream.readInt32(exception); + thumb = PhotoSize.TLdeserialize(stream, stream.readInt32(exception), exception); + dc_id = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + DocumentAttribute object = DocumentAttribute.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + attributes.add(object); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(date); + stream.writeString(mime_type); + stream.writeInt32(size); + thumb.serializeToStream(stream); + stream.writeInt32(dc_id); + stream.writeInt32(0x1cb5c415); + int count = attributes.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + attributes.get(a).serializeToStream(stream); + } + } + } + + public static class MessagesFilter extends TLObject { + + public static MessagesFilter TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + MessagesFilter result = null; + switch(constructor) { + case 0x9eddf188: + result = new TL_inputMessagesFilterDocument(); + break; + case 0x9fc00e65: + result = new TL_inputMessagesFilterVideo(); + break; + case 0x9609a51c: + result = new TL_inputMessagesFilterPhotos(); + break; + case 0xd95e73bb: + result = new TL_inputMessagesFilterPhotoVideoDocuments(); + break; + case 0xcfc87522: + result = new TL_inputMessagesFilterAudio(); + break; + case 0x57e2f66c: + result = new TL_inputMessagesFilterEmpty(); + break; + case 0x56e9f0e4: + result = new TL_inputMessagesFilterPhotoVideo(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in MessagesFilter", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inputMessagesFilterDocument extends MessagesFilter { + public static int constructor = 0x9eddf188; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputMessagesFilterVideo extends MessagesFilter { + public static int constructor = 0x9fc00e65; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputMessagesFilterPhotos extends MessagesFilter { + public static int constructor = 0x9609a51c; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputMessagesFilterPhotoVideoDocuments extends MessagesFilter { + public static int constructor = 0xd95e73bb; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputMessagesFilterAudio extends MessagesFilter { + public static int constructor = 0xcfc87522; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputMessagesFilterEmpty extends MessagesFilter { + public static int constructor = 0x57e2f66c; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputMessagesFilterPhotoVideo extends MessagesFilter { + public static int constructor = 0x56e9f0e4; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_account_passwordSettings extends TLObject { + public static int constructor = 0xb7b72ab3; + + public String email; + + public static TL_account_passwordSettings TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_account_passwordSettings.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_account_passwordSettings", constructor)); + } else { + return null; + } + } + TL_account_passwordSettings result = new TL_account_passwordSettings(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + email = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(email); + } + } + + public static class ExportedChatInvite extends TLObject { + public String link; + + public static ExportedChatInvite TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + ExportedChatInvite result = null; + switch(constructor) { + case 0xfc2e05bc: + result = new TL_chatInviteExported(); + break; + case 0x69df3769: + result = new TL_chatInviteEmpty(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in ExportedChatInvite", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_chatInviteExported extends ExportedChatInvite { + public static int constructor = 0xfc2e05bc; + + + public void readParams(AbsSerializedData stream, boolean exception) { + link = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(link); + } + } + + public static class TL_chatInviteEmpty extends ExportedChatInvite { + public static int constructor = 0x69df3769; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_http_wait extends TLObject { + public static int constructor = 0x9299359f; + + public int max_delay; + public int wait_after; + public int max_wait; + + public static TL_http_wait TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_http_wait.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_http_wait", constructor)); + } else { + return null; + } + } + TL_http_wait result = new TL_http_wait(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + max_delay = stream.readInt32(exception); + wait_after = stream.readInt32(exception); + max_wait = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(max_delay); + stream.writeInt32(wait_after); + stream.writeInt32(max_wait); + } + } + public static class InputPhotoCrop extends TLObject { public double crop_left; public double crop_top; public double crop_width; + + public static InputPhotoCrop TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + InputPhotoCrop result = null; + switch(constructor) { + case 0xade6b004: + result = new TL_inputPhotoCropAuto(); + break; + case 0xd9915325: + result = new TL_inputPhotoCrop(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputPhotoCrop", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } } public static class TL_inputPhotoCropAuto extends InputPhotoCrop { @@ -7408,10 +11404,10 @@ public class TLRPC { public static int constructor = 0xd9915325; - public void readParams(AbsSerializedData stream) { - crop_left = stream.readDouble(); - crop_top = stream.readDouble(); - crop_width = stream.readDouble(); + public void readParams(AbsSerializedData stream, boolean exception) { + crop_left = stream.readDouble(exception); + crop_top = stream.readDouble(exception); + crop_width = stream.readDouble(exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7422,60 +11418,847 @@ public class TLRPC { } } - public static class messages_Dialogs extends TLObject { - public ArrayList dialogs = new ArrayList<>(); - public ArrayList messages = new ArrayList<>(); + public static class Chat extends TLObject { + public int id; + public String title; + public int date; + public long access_hash; + public String address; + public String venue; + public GeoPoint geo; + public ChatPhoto photo; + public int participants_count; + public boolean checked_in; + public int version; + public boolean left; + + public static Chat TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + Chat result = null; + switch(constructor) { + case 0xfb0ccc41: + result = new TL_chatForbidden(); + break; + case 0x75eaea5a: + result = new TL_geoChat(); + break; + case 0x9ba2d800: + result = new TL_chatEmpty(); + break; + case 0x6e9c9bc7: + result = new TL_chat(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in Chat", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_chatForbidden extends Chat { + public static int constructor = 0xfb0ccc41; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + title = stream.readString(exception); + date = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeString(title); + stream.writeInt32(date); + } + } + + public static class TL_geoChat extends Chat { + public static int constructor = 0x75eaea5a; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + access_hash = stream.readInt64(exception); + title = stream.readString(exception); + address = stream.readString(exception); + venue = stream.readString(exception); + geo = GeoPoint.TLdeserialize(stream, stream.readInt32(exception), exception); + photo = ChatPhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + participants_count = stream.readInt32(exception); + date = stream.readInt32(exception); + checked_in = stream.readBool(exception); + version = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeInt64(access_hash); + stream.writeString(title); + stream.writeString(address); + stream.writeString(venue); + geo.serializeToStream(stream); + photo.serializeToStream(stream); + stream.writeInt32(participants_count); + stream.writeInt32(date); + stream.writeBool(checked_in); + stream.writeInt32(version); + } + } + + public static class TL_chat extends Chat { + public static int constructor = 0x6e9c9bc7; + + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + title = stream.readString(exception); + photo = ChatPhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + participants_count = stream.readInt32(exception); + date = stream.readInt32(exception); + left = stream.readBool(exception); + version = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeString(title); + photo.serializeToStream(stream); + stream.writeInt32(participants_count); + stream.writeInt32(date); + stream.writeBool(left); + stream.writeInt32(version); + } + } + + public static class TL_messages_chats extends TLObject { + public static int constructor = 0x64ff9fd5; + public ArrayList chats = new ArrayList<>(); + + public static TL_messages_chats TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_messages_chats.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_messages_chats", constructor)); + } else { + return null; + } + } + TL_messages_chats result = new TL_messages_chats(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Chat object = Chat.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + chats.add(object); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.get(a).serializeToStream(stream); + } + } + } + + public static class ContactLink extends TLObject { + + public static ContactLink TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + ContactLink result = null; + switch(constructor) { + case 0xfeedd3ad: + result = new TL_contactLinkNone(); + break; + case 0xd502c2d0: + result = new TL_contactLinkContact(); + break; + case 0x268f3f59: + result = new TL_contactLinkHasPhone(); + break; + case 0x5f4f9247: + result = new TL_contactLinkUnknown(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in ContactLink", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_contactLinkNone extends ContactLink { + public static int constructor = 0xfeedd3ad; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_contactLinkContact extends ContactLink { + public static int constructor = 0xd502c2d0; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_contactLinkHasPhone extends ContactLink { + public static int constructor = 0x268f3f59; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_contactLinkUnknown extends ContactLink { + public static int constructor = 0x5f4f9247; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class PeerNotifyEvents extends TLObject { + + public static PeerNotifyEvents TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + PeerNotifyEvents result = null; + switch(constructor) { + case 0xadd53cb3: + result = new TL_peerNotifyEventsEmpty(); + break; + case 0x6d1ded88: + result = new TL_peerNotifyEventsAll(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in PeerNotifyEvents", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_peerNotifyEventsEmpty extends PeerNotifyEvents { + public static int constructor = 0xadd53cb3; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_peerNotifyEventsAll extends PeerNotifyEvents { + public static int constructor = 0x6d1ded88; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class SendMessageAction extends TLObject { + public int progress; + + public static SendMessageAction TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + SendMessageAction result = null; + switch(constructor) { + case 0xd52f73f7: + result = new TL_sendMessageRecordAudioAction(); + break; + case 0x92042ff7: + result = new TL_sendMessageUploadVideoAction_old(); + break; + case 0xe6ac8a6f: + result = new TL_sendMessageUploadAudioAction_old(); + break; + case 0xf351d7ab: + result = new TL_sendMessageUploadAudioAction(); + break; + case 0xd1d34a26: + result = new TL_sendMessageUploadPhotoAction(); + break; + case 0x8faee98e: + result = new TL_sendMessageUploadDocumentAction_old(); + break; + case 0xe9763aec: + result = new TL_sendMessageUploadVideoAction(); + break; + case 0xfd5ec8f5: + result = new TL_sendMessageCancelAction(); + break; + case 0x176f8ba1: + result = new TL_sendMessageGeoLocationAction(); + break; + case 0x628cbc6f: + result = new TL_sendMessageChooseContactAction(); + break; + case 0x16bf744e: + result = new TL_sendMessageTypingAction(); + break; + case 0x990a3c1a: + result = new TL_sendMessageUploadPhotoAction_old(); + break; + case 0xaa0cd9e4: + result = new TL_sendMessageUploadDocumentAction(); + break; + case 0xa187d66f: + result = new TL_sendMessageRecordVideoAction(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in SendMessageAction", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_sendMessageRecordAudioAction extends SendMessageAction { + public static int constructor = 0xd52f73f7; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_sendMessageUploadVideoAction_old extends TL_sendMessageUploadVideoAction { + public static int constructor = 0x92042ff7; + + public void readParams(AbsSerializedData stream, boolean exception) { + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_sendMessageUploadAudioAction_old extends TL_sendMessageUploadAudioAction { + public static int constructor = 0xe6ac8a6f; + + public void readParams(AbsSerializedData stream, boolean exception) { + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_sendMessageUploadAudioAction extends SendMessageAction { + public static int constructor = 0xf351d7ab; + + + public void readParams(AbsSerializedData stream, boolean exception) { + progress = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(progress); + } + } + + public static class TL_sendMessageUploadPhotoAction extends SendMessageAction { + public static int constructor = 0xd1d34a26; + + + public void readParams(AbsSerializedData stream, boolean exception) { + progress = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(progress); + } + } + + public static class TL_sendMessageUploadDocumentAction_old extends TL_sendMessageUploadDocumentAction { + public static int constructor = 0x8faee98e; + + public void readParams(AbsSerializedData stream, boolean exception) { + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_sendMessageUploadVideoAction extends SendMessageAction { + public static int constructor = 0xe9763aec; + + + public void readParams(AbsSerializedData stream, boolean exception) { + progress = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(progress); + } + } + + public static class TL_sendMessageCancelAction extends SendMessageAction { + public static int constructor = 0xfd5ec8f5; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_sendMessageGeoLocationAction extends SendMessageAction { + public static int constructor = 0x176f8ba1; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_sendMessageChooseContactAction extends SendMessageAction { + public static int constructor = 0x628cbc6f; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_sendMessageTypingAction extends SendMessageAction { + public static int constructor = 0x16bf744e; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_sendMessageUploadPhotoAction_old extends TL_sendMessageUploadPhotoAction { + public static int constructor = 0x990a3c1a; + + public void readParams(AbsSerializedData stream, boolean exception) { + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_sendMessageUploadDocumentAction extends SendMessageAction { + public static int constructor = 0xaa0cd9e4; + + + public void readParams(AbsSerializedData stream, boolean exception) { + progress = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(progress); + } + } + + public static class TL_sendMessageRecordVideoAction extends SendMessageAction { + public static int constructor = 0xa187d66f; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_client_DH_inner_data extends TLObject { + public static int constructor = 0x6643b654; + + public byte[] nonce; + public byte[] server_nonce; + public long retry_id; + public byte[] g_b; + + public static TL_client_DH_inner_data TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_client_DH_inner_data.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_client_DH_inner_data", constructor)); + } else { + return null; + } + } + TL_client_DH_inner_data result = new TL_client_DH_inner_data(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + nonce = stream.readData(16, exception); + server_nonce = stream.readData(16, exception); + retry_id = stream.readInt64(exception); + g_b = stream.readByteArray(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeRaw(nonce); + stream.writeRaw(server_nonce); + stream.writeInt64(retry_id); + stream.writeByteArray(g_b); + } + } + + public static class TL_null extends TLObject { + public static int constructor = 0x56730bcc; + + + public static TL_null TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_null.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_null", constructor)); + } else { + return null; + } + } + TL_null result = new TL_null(); + result.readParams(stream, exception); + return result; + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class account_Password extends TLObject { + public byte[] current_salt; + public byte[] new_salt; + public String hint; + public boolean has_recovery; + public String email_unconfirmed_pattern; + + public static account_Password TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + account_Password result = null; + switch(constructor) { + case 0x7c18141c: + result = new TL_account_password(); + break; + case 0x96dabc18: + result = new TL_account_noPassword(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in account_Password", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_account_password extends account_Password { + public static int constructor = 0x7c18141c; + + + public void readParams(AbsSerializedData stream, boolean exception) { + current_salt = stream.readByteArray(exception); + new_salt = stream.readByteArray(exception); + hint = stream.readString(exception); + has_recovery = stream.readBool(exception); + email_unconfirmed_pattern = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeByteArray(current_salt); + stream.writeByteArray(new_salt); + stream.writeString(hint); + stream.writeBool(has_recovery); + stream.writeString(email_unconfirmed_pattern); + } + } + + public static class TL_account_noPassword extends account_Password { + public static int constructor = 0x96dabc18; + + + public void readParams(AbsSerializedData stream, boolean exception) { + new_salt = stream.readByteArray(exception); + email_unconfirmed_pattern = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeByteArray(new_salt); + stream.writeString(email_unconfirmed_pattern); + } + } + + public static class DocumentAttribute extends TLObject { + public int w; + public int h; + public String file_name; + public String alt; + public InputStickerSet stickerset; + public int duration; + + public static DocumentAttribute TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + DocumentAttribute result = null; + switch(constructor) { + case 0x11b58939: + result = new TL_documentAttributeAnimated(); + break; + case 0xfb0a5727: + result = new TL_documentAttributeSticker_old(); + break; + case 0x6c37c15c: + result = new TL_documentAttributeImageSize(); + break; + case 0x15590068: + result = new TL_documentAttributeFilename(); + break; + case 0x3a556302: + result = new TL_documentAttributeSticker(); + break; + case 0x5910cccb: + result = new TL_documentAttributeVideo(); + break; + case 0x51448e5: + result = new TL_documentAttributeAudio(); + break; + case 0x994c9882: + result = new TL_documentAttributeSticker_old2(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in DocumentAttribute", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_documentAttributeAnimated extends DocumentAttribute { + public static int constructor = 0x11b58939; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_documentAttributeSticker_old extends TL_documentAttributeSticker { + public static int constructor = 0xfb0a5727; + + public void readParams(AbsSerializedData stream, boolean exception) { + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_documentAttributeImageSize extends DocumentAttribute { + public static int constructor = 0x6c37c15c; + + + public void readParams(AbsSerializedData stream, boolean exception) { + w = stream.readInt32(exception); + h = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(w); + stream.writeInt32(h); + } + } + + public static class TL_documentAttributeFilename extends DocumentAttribute { + public static int constructor = 0x15590068; + + + public void readParams(AbsSerializedData stream, boolean exception) { + file_name = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(file_name); + } + } + + public static class TL_documentAttributeSticker extends DocumentAttribute { + public static int constructor = 0x3a556302; + + + public void readParams(AbsSerializedData stream, boolean exception) { + alt = stream.readString(exception); + stickerset = InputStickerSet.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(alt); + stickerset.serializeToStream(stream); + } + } + + public static class TL_documentAttributeVideo extends DocumentAttribute { + public static int constructor = 0x5910cccb; + + + public void readParams(AbsSerializedData stream, boolean exception) { + duration = stream.readInt32(exception); + w = stream.readInt32(exception); + h = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(duration); + stream.writeInt32(w); + stream.writeInt32(h); + } + } + + public static class TL_documentAttributeAudio extends DocumentAttribute { + public static int constructor = 0x51448e5; + + + public void readParams(AbsSerializedData stream, boolean exception) { + duration = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(duration); + } + } + + public static class TL_documentAttributeSticker_old2 extends TL_documentAttributeSticker { + public static int constructor = 0x994c9882; + + + public void readParams(AbsSerializedData stream, boolean exception) { + alt = stream.readString(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(alt); + } + } + + public static class TL_contacts_importedContacts extends TLObject { + public static int constructor = 0xad524315; + + public ArrayList imported = new ArrayList<>(); + public ArrayList retry_contacts = new ArrayList<>(); public ArrayList users = new ArrayList<>(); - public int count; - } - public static class TL_messages_dialogs extends messages_Dialogs { - public static int constructor = 0x15ba6c40; + public static TL_contacts_importedContacts TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_contacts_importedContacts.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_contacts_importedContacts", constructor)); + } else { + return null; + } + } + TL_contacts_importedContacts result = new TL_contacts_importedContacts(); + result.readParams(stream, exception); + return result; + } - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - dialogs.add((TL_dialog)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; } - stream.readInt32(); - count = stream.readInt32(); + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - messages.add((Message)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + TL_importedContact object = TL_importedContact.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + imported.add(object); } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - chats.add((Chat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; } - stream.readInt32(); - count = stream.readInt32(); + count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + retry_contacts.add(stream.readInt64(exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); } } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); - int count = dialogs.size(); + int count = imported.size(); stream.writeInt32(count); for (int a = 0; a < count; a++) { - dialogs.get(a).serializeToStream(stream); + imported.get(a).serializeToStream(stream); } stream.writeInt32(0x1cb5c415); - count = messages.size(); + count = retry_contacts.size(); stream.writeInt32(count); for (int a = 0; a < count; a++) { - messages.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = chats.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - chats.get(a).serializeToStream(stream); + stream.writeInt64(retry_contacts.get(a)); } stream.writeInt32(0x1cb5c415); count = users.size(); @@ -7486,99 +12269,252 @@ public class TLRPC { } } - public static class TL_messages_dialogsSlice extends messages_Dialogs { - public static int constructor = 0x71e094f3; + public static class PrivacyRule extends TLObject { + public ArrayList users = new ArrayList<>(); + + public static PrivacyRule TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + PrivacyRule result = null; + switch(constructor) { + case 0x4d5bbe0c: + result = new TL_privacyValueAllowUsers(); + break; + case 0x8b73e763: + result = new TL_privacyValueDisallowAll(); + break; + case 0xfffe1bac: + result = new TL_privacyValueAllowContacts(); + break; + case 0xf888fa1a: + result = new TL_privacyValueDisallowContacts(); + break; + case 0x65427b82: + result = new TL_privacyValueAllowAll(); + break; + case 0xc7f49b7: + result = new TL_privacyValueDisallowUsers(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in PrivacyRule", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_privacyValueAllowUsers extends PrivacyRule { + public static int constructor = 0x4d5bbe0c; - public void readParams(AbsSerializedData stream) { - count = stream.readInt32(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - dialogs.add((TL_dialog)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + public void readParams(AbsSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; } - stream.readInt32(); - count = stream.readInt32(); + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - messages.add((Message)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - chats.add((Chat)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())); + users.add(stream.readInt32(exception)); } } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(count); stream.writeInt32(0x1cb5c415); - int count = dialogs.size(); + int count = users.size(); stream.writeInt32(count); for (int a = 0; a < count; a++) { - dialogs.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = messages.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - messages.get(a).serializeToStream(stream); - } - stream.writeInt32(0x1cb5c415); - count = chats.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - chats.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); + stream.writeInt32(users.get(a)); } } } - public static class TL_account_authorizations extends TLObject { - public static int constructor = 0x1250abde; + public static class TL_privacyValueDisallowAll extends PrivacyRule { + public static int constructor = 0x8b73e763; - public ArrayList authorizations = new ArrayList<>(); - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); + 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, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - authorizations.add((TL_authorization)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + users.add(stream.readInt32(exception)); } } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); - int count = authorizations.size(); + int count = users.size(); stream.writeInt32(count); for (int a = 0; a < count; a++) { - authorizations.get(a).serializeToStream(stream); + stream.writeInt32(users.get(a)); } } } + public static class TL_updates_state extends TLObject { + public static int constructor = 0xa56c2a3e; + + public int pts; + public int qts; + public int date; + public int seq; + public int unread_count; + + public static TL_updates_state TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_updates_state.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_updates_state", constructor)); + } else { + return null; + } + } + TL_updates_state result = new TL_updates_state(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + pts = stream.readInt32(exception); + qts = stream.readInt32(exception); + date = stream.readInt32(exception); + seq = stream.readInt32(exception); + unread_count = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(pts); + stream.writeInt32(qts); + stream.writeInt32(date); + stream.writeInt32(seq); + stream.writeInt32(unread_count); + } + } + + public static class TL_destroy_sessions_res extends TLObject { + public static int constructor = 0xfb95abcd; + + public ArrayList destroy_results = new ArrayList<>(); + + public static TL_destroy_sessions_res TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_destroy_sessions_res.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_destroy_sessions_res", constructor)); + } else { + return null; + } + } + TL_destroy_sessions_res result = new TL_destroy_sessions_res(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + DestroySessionRes object = DestroySessionRes.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + destroy_results.add(object); + } + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + int count = destroy_results.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + destroy_results.get(a).serializeToStream(stream); + } + } + } + + public static class TL_receivedNotifyMessage extends TLObject { + public static int constructor = 0xa384b779; + + public int id; + public int flags; + + public static TL_receivedNotifyMessage TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_receivedNotifyMessage.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_receivedNotifyMessage", constructor)); + } else { + return null; + } + } + TL_receivedNotifyMessage result = new TL_receivedNotifyMessage(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + flags = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeInt32(flags); + } + } + public static class TL_req_pq extends TLObject { public static int constructor = 0x60469778; public byte[] nonce; - public Class responseClass () { - return TL_resPQ.class; - } - - public void readParams(AbsSerializedData stream) { - nonce = stream.readData(16); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_resPQ.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7597,17 +12533,8 @@ public class TLRPC { public long public_key_fingerprint; public byte[] encrypted_data; - public Class responseClass () { - return Server_DH_Params.class; - } - - public void readParams(AbsSerializedData stream) { - nonce = stream.readData(16); - server_nonce = stream.readData(16); - p = stream.readByteArray(); - q = stream.readByteArray(); - public_key_fingerprint = stream.readInt64(); - encrypted_data = stream.readByteArray(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Server_DH_Params.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7626,12 +12553,8 @@ public class TLRPC { public String phone_number; - public Class responseClass () { - return TL_auth_checkedPhone.class; - } - - public void readParams(AbsSerializedData stream) { - phone_number = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_auth_checkedPhone.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7649,16 +12572,8 @@ public class TLRPC { public String api_hash; public String lang_code; - public Class responseClass () { - return TL_auth_sentCode.class; - } - - public void readParams(AbsSerializedData stream) { - phone_number = stream.readString(); - sms_type = stream.readInt32(); - api_id = stream.readInt32(); - api_hash = stream.readString(); - lang_code = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return auth_SentCode.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7677,13 +12592,8 @@ public class TLRPC { public String phone_number; public String phone_code_hash; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - phone_number = stream.readString(); - phone_code_hash = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7702,16 +12612,8 @@ public class TLRPC { public String first_name; public String last_name; - public Class responseClass () { - return TL_auth_authorization.class; - } - - public void readParams(AbsSerializedData stream) { - phone_number = stream.readString(); - phone_code_hash = stream.readString(); - phone_code = stream.readString(); - first_name = stream.readString(); - last_name = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_auth_authorization.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7731,14 +12633,8 @@ public class TLRPC { public String phone_code_hash; public String phone_code; - public Class responseClass () { - return TL_auth_authorization.class; - } - - public void readParams(AbsSerializedData stream) { - phone_number = stream.readString(); - phone_code_hash = stream.readString(); - phone_code = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_auth_authorization.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7753,8 +12649,8 @@ public class TLRPC { public static int constructor = 0x5717da40; - public Class responseClass () { - return Bool.class; + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7766,8 +12662,8 @@ public class TLRPC { public static int constructor = 0x9fab0d1a; - public Class responseClass () { - return Bool.class; + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7781,17 +12677,8 @@ public class TLRPC { public ArrayList phone_numbers = new ArrayList<>(); public String message; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - phone_numbers.add(stream.readString()); - } - message = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7799,8 +12686,8 @@ public class TLRPC { stream.writeInt32(0x1cb5c415); int count = phone_numbers.size(); stream.writeInt32(count); - for (String phone_number : phone_numbers) { - stream.writeString(phone_number); + for (int a = 0; a < count; a++) { + stream.writeString(phone_numbers.get(a)); } stream.writeString(message); } @@ -7811,12 +12698,8 @@ public class TLRPC { public int dc_id; - public Class responseClass () { - return TL_auth_exportedAuthorization.class; - } - - public void readParams(AbsSerializedData stream) { - dc_id = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_auth_exportedAuthorization.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7831,13 +12714,8 @@ public class TLRPC { public int id; public byte[] bytes; - public Class responseClass () { - return TL_auth_authorization.class; - } - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - bytes = stream.readByteArray(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_auth_authorization.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7847,6 +12725,27 @@ public class TLRPC { } } + public static class TL_auth_bindTempAuthKey extends TLObject { + public static int constructor = 0xcdd42a05; + + public long perm_auth_key_id; + public long nonce; + public int expires_at; + public byte[] encrypted_message; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(perm_auth_key_id); + stream.writeInt64(nonce); + stream.writeInt32(expires_at); + stream.writeByteArray(encrypted_message); + } + } + public static class TL_account_registerDevice extends TLObject { public static int constructor = 0x446c712c; @@ -7858,18 +12757,8 @@ public class TLRPC { public boolean app_sandbox; public String lang_code; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - token_type = stream.readInt32(); - token = stream.readString(); - device_model = stream.readString(); - system_version = stream.readString(); - app_version = stream.readString(); - app_sandbox = stream.readBool(); - lang_code = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7890,13 +12779,8 @@ public class TLRPC { public int token_type; public String token; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - token_type = stream.readInt32(); - token = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7912,13 +12796,8 @@ public class TLRPC { public InputNotifyPeer peer; public TL_inputPeerNotifySettings settings; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (InputNotifyPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - settings = (TL_inputPeerNotifySettings)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7933,12 +12812,8 @@ public class TLRPC { public InputNotifyPeer peer; - public Class responseClass () { - return PeerNotifySettings.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (InputNotifyPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return PeerNotifySettings.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7951,8 +12826,8 @@ public class TLRPC { public static int constructor = 0xdb7e1747; - public Class responseClass () { - return Bool.class; + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7966,13 +12841,8 @@ public class TLRPC { public String first_name; public String last_name; - public Class responseClass () { - return User.class; - } - - public void readParams(AbsSerializedData stream) { - first_name = stream.readString(); - last_name = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return User.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -7987,12 +12857,8 @@ public class TLRPC { public boolean offline; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - offline = stream.readBool(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8001,17 +12867,44 @@ public class TLRPC { } } + public static class TL_account_getWallPapers extends TLObject { + public static int constructor = 0xc04cfac2; + + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + Vector vector = new Vector(); + int size = stream.readInt32(exception); + for (int a = 0; a < size; a++) { + WallPaper object = WallPaper.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return vector; + } + vector.objects.add(object); + } + return vector; + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + public static class TL_users_getUsers extends TLObject { public static int constructor = 0xd91a548; public ArrayList id = new ArrayList<>(); - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - id.add((InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + Vector vector = new Vector(); + int size = stream.readInt32(exception); + for (int a = 0; a < size; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return vector; + } + vector.objects.add(object); } + return vector; } public void serializeToStream(AbsSerializedData stream) { @@ -8019,8 +12912,8 @@ public class TLRPC { stream.writeInt32(0x1cb5c415); int count = id.size(); stream.writeInt32(count); - for (InputUser anId : id) { - anId.serializeToStream(stream); + for (int a = 0; a < count; a++) { + id.get(a).serializeToStream(stream); } } } @@ -8030,12 +12923,8 @@ public class TLRPC { public InputUser id; - public Class responseClass () { - return TL_userFull.class; - } - - public void readParams(AbsSerializedData stream) { - id = (InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_userFull.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8047,17 +12936,18 @@ public class TLRPC { public static class TL_contacts_getStatuses extends TLObject { public static int constructor = 0xc4a353ee; - public ArrayList id = new ArrayList<>(); - public Class responseClass () { - return Vector.class; - } - - public void parseVector(Vector vector, AbsSerializedData data) { - int size = data.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + Vector vector = new Vector(); + int size = stream.readInt32(exception); for (int a = 0; a < size; a++) { - vector.objects.add(TLClassStore.Instance().TLdeserialize(data, data.readInt32())); + TL_contactStatus object = TL_contactStatus.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return vector; + } + vector.objects.add(object); } + return vector; } public void serializeToStream(AbsSerializedData stream) { @@ -8070,12 +12960,8 @@ public class TLRPC { public String hash; - public Class responseClass () { - return contacts_Contacts.class; - } - - public void readParams(AbsSerializedData stream) { - hash = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return contacts_Contacts.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8090,17 +12976,8 @@ public class TLRPC { public ArrayList contacts = new ArrayList<>(); public boolean replace; - public Class responseClass () { - return TL_contacts_importedContacts.class; - } - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - contacts.add((TL_inputPhoneContact)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - replace = stream.readBool(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_contacts_importedContacts.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8108,46 +12985,20 @@ public class TLRPC { stream.writeInt32(0x1cb5c415); int count = contacts.size(); stream.writeInt32(count); - for (TL_inputPhoneContact contact : contacts) { - contact.serializeToStream(stream); + for (int a = 0; a < count; a++) { + contacts.get(a).serializeToStream(stream); } stream.writeBool(replace); } } - public static class TL_contacts_search extends TLObject { - public static int constructor = 0x11f812d8; - - public String q; - public int limit; - - public Class responseClass () { - return TL_contacts_found.class; - } - - public void readParams(AbsSerializedData stream) { - q = stream.readString(); - limit = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(q); - stream.writeInt32(limit); - } - } - public static class TL_contacts_getSuggested extends TLObject { public static int constructor = 0xcd773428; public int limit; - public Class responseClass () { - return TL_contacts_suggested.class; - } - - public void readParams(AbsSerializedData stream) { - limit = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_contacts_suggested.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8161,12 +13012,8 @@ public class TLRPC { public InputUser id; - public Class responseClass () { - return TL_contacts_link.class; - } - - public void readParams(AbsSerializedData stream) { - id = (InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_contacts_link.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8180,16 +13027,8 @@ public class TLRPC { public ArrayList id = new ArrayList<>(); - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - id.add((InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8197,8 +13036,8 @@ public class TLRPC { stream.writeInt32(0x1cb5c415); int count = id.size(); stream.writeInt32(count); - for (InputUser anId : id) { - anId.serializeToStream(stream); + for (int a = 0; a < count; a++) { + id.get(a).serializeToStream(stream); } } } @@ -8208,12 +13047,8 @@ public class TLRPC { public InputUser id; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - id = (InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8227,12 +13062,8 @@ public class TLRPC { public InputUser id; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - id = (InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8247,13 +13078,8 @@ public class TLRPC { public int offset; public int limit; - public Class responseClass () { - return contacts_Blocked.class; - } - - public void readParams(AbsSerializedData stream) { - offset = stream.readInt32(); - limit = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return contacts_Blocked.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8263,21 +13089,51 @@ public class TLRPC { } } + public static class TL_contacts_exportCard extends TLObject { + public static int constructor = 0x84e53737; + + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + Vector vector = new Vector(); + int size = stream.readInt32(exception); + for (int a = 0; a < size; a++) { + vector.objects.add(stream.readInt32(exception)); + } + return vector; + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_contacts_importCard extends TLObject { + public static int constructor = 0x4fe196fe; + + public ArrayList export_card = new ArrayList<>(); + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return User.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = export_card.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt32(export_card.get(a)); + } + } + } + public static class TL_messages_getMessages extends TLObject { public static int constructor = 0x4222fa74; public ArrayList id = new ArrayList<>(); - public Class responseClass () { - return messages_Messages.class; - } - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - id.add(stream.readInt32()); - } + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return messages_Messages.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8285,8 +13141,8 @@ public class TLRPC { stream.writeInt32(0x1cb5c415); int count = id.size(); stream.writeInt32(count); - for (Integer anId : id) { - stream.writeInt32(anId); + for (int a = 0; a < count; a++) { + stream.writeInt32(id.get(a)); } } } @@ -8298,14 +13154,8 @@ public class TLRPC { public int max_id; public int limit; - public Class responseClass () { - return messages_Dialogs.class; - } - - public void readParams(AbsSerializedData stream) { - offset = stream.readInt32(); - max_id = stream.readInt32(); - limit = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return messages_Dialogs.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8324,15 +13174,8 @@ public class TLRPC { public int max_id; public int limit; - public Class responseClass () { - return messages_Messages.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - offset = stream.readInt32(); - max_id = stream.readInt32(); - limit = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return messages_Messages.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8356,19 +13199,8 @@ public class TLRPC { public int max_id; public int limit; - public Class responseClass () { - return messages_Messages.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - q = stream.readString(); - filter = (MessagesFilter)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - min_date = stream.readInt32(); - max_date = stream.readInt32(); - offset = stream.readInt32(); - max_id = stream.readInt32(); - limit = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return messages_Messages.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8391,14 +13223,8 @@ public class TLRPC { public int max_id; public int offset; - public Class responseClass () { - return TL_messages_affectedHistory.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - max_id = stream.readInt32(); - offset = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_messages_affectedHistory.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8415,13 +13241,8 @@ public class TLRPC { public InputPeer peer; public int offset; - public Class responseClass () { - return TL_messages_affectedHistory.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - offset = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_messages_affectedHistory.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8431,19 +13252,58 @@ public class TLRPC { } } + public static class TL_messages_deleteMessages extends TLObject { + public static int constructor = 0xa5f18925; + + public ArrayList id = new ArrayList<>(); + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_messages_affectedMessages.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = id.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt32(id.get(a)); + } + } + } + + public static class TL_messages_receivedMessages extends TLObject { + public static int constructor = 0x5a954c0; + + public int max_id; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + Vector vector = new Vector(); + int size = stream.readInt32(exception); + for (int a = 0; a < size; a++) { + TL_receivedNotifyMessage object = TL_receivedNotifyMessage.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return vector; + } + vector.objects.add(object); + } + return vector; + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(max_id); + } + } + public static class TL_messages_setTyping extends TLObject { public static int constructor = 0xa3825e50; public InputPeer peer; public SendMessageAction action; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - action = (SendMessageAction)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8462,18 +13322,8 @@ public class TLRPC { public String message; public long random_id; - public Class responseClass () { - return messages_SentMessage.class; - } - - public void readParams(AbsSerializedData stream) { - flags = stream.readInt32(); - peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - if ((flags & 1) != 0) { - reply_to_msg_id = stream.readInt32(); - } - message = stream.readString(); - random_id = stream.readInt64(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return messages_SentMessage.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8497,18 +13347,8 @@ public class TLRPC { public InputMedia media; public long random_id; - public Class responseClass () { - return Updates.class; - } - - public void readParams(AbsSerializedData stream) { - flags = stream.readInt32(); - peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - if ((flags & 1) != 0) { - reply_to_msg_id = stream.readInt32(); - } - media = (InputMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - random_id = stream.readInt64(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8530,22 +13370,8 @@ public class TLRPC { public ArrayList id = new ArrayList<>(); public ArrayList random_id = new ArrayList<>(); - public Class responseClass () { - return Updates.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - id.add(stream.readInt32()); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - random_id.add(stream.readInt64()); - } + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8571,16 +13397,8 @@ public class TLRPC { public ArrayList id = new ArrayList<>(); - public Class responseClass () { - return TL_messages_chats.class; - } - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - id.add(stream.readInt32()); - } + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_messages_chats.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8588,8 +13406,8 @@ public class TLRPC { stream.writeInt32(0x1cb5c415); int count = id.size(); stream.writeInt32(count); - for (Integer anId : id) { - stream.writeInt32(anId); + for (int a = 0; a < count; a++) { + stream.writeInt32(id.get(a)); } } } @@ -8599,12 +13417,8 @@ public class TLRPC { public int chat_id; - public Class responseClass () { - return TL_messages_chatFull.class; - } - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_messages_chatFull.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8619,13 +13433,8 @@ public class TLRPC { public int chat_id; public String title; - public Class responseClass () { - return Updates.class; - } - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - title = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8641,13 +13450,8 @@ public class TLRPC { public int chat_id; public InputChatPhoto photo; - public Class responseClass () { - return Updates.class; - } - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - photo = (InputChatPhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8664,14 +13468,8 @@ public class TLRPC { public InputUser user_id; public int fwd_limit; - public Class responseClass () { - return Updates.class; - } - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - user_id = (InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - fwd_limit = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8688,13 +13486,8 @@ public class TLRPC { public int chat_id; public InputUser user_id; - public Class responseClass () { - return Updates.class; - } - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); - user_id = (InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8710,17 +13503,8 @@ public class TLRPC { public ArrayList users = new ArrayList<>(); public String title; - public Class responseClass () { - return Updates.class; - } - - 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())); - } - title = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8739,8 +13523,8 @@ public class TLRPC { public static int constructor = 0xedd4882a; - public Class responseClass () { - return TL_updates_state.class; + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_updates_state.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8755,14 +13539,8 @@ public class TLRPC { public int date; public int qts; - public Class responseClass () { - return updates_Difference.class; - } - - public void readParams(AbsSerializedData stream) { - pts = stream.readInt32(); - date = stream.readInt32(); - qts = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return updates_Difference.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8779,13 +13557,8 @@ public class TLRPC { public InputPhoto id; public InputPhotoCrop crop; - public Class responseClass () { - return UserProfilePhoto.class; - } - - public void readParams(AbsSerializedData stream) { - id = (InputPhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - crop = (InputPhotoCrop)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return UserProfilePhoto.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8795,32 +13568,6 @@ public class TLRPC { } } - public static class TL_photos_deletePhotos extends TLObject { - public static int constructor = 0x87cf7f2f; - - public ArrayList id = new ArrayList<>(); - - 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(data.readInt64()); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - stream.writeInt32(id.size()); - for (InputPhoto inputPhoto : id) { - inputPhoto.serializeToStream(stream); - } - } - } - public static class TL_photos_uploadProfilePhoto extends TLObject { public static int constructor = 0xd50f9c88; @@ -8829,15 +13576,8 @@ public class TLRPC { public InputGeoPoint geo_point; public InputPhotoCrop crop; - public Class responseClass () { - return TL_photos_photo.class; - } - - public void readParams(AbsSerializedData stream) { - file = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - caption = stream.readString(); - geo_point = (InputGeoPoint)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - crop = (InputPhotoCrop)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_photos_photo.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8849,6 +13589,31 @@ public class TLRPC { } } + public static class TL_photos_deletePhotos extends TLObject { + public static int constructor = 0x87cf7f2f; + + public ArrayList id = new ArrayList<>(); + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + Vector vector = new Vector(); + int size = stream.readInt32(exception); + for (int a = 0; a < size; a++) { + vector.objects.add(stream.readInt64(exception)); + } + return vector; + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = id.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + id.get(a).serializeToStream(stream); + } + } + } + public static class TL_upload_getFile extends TLObject { public static int constructor = 0xe3a6cfb5; @@ -8856,14 +13621,8 @@ public class TLRPC { public int offset; public int limit; - public Class responseClass() { - return TL_upload_file.class; - } - - public void readParams(AbsSerializedData stream) { - location = (InputFileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - offset = stream.readInt32(); - limit = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_upload_file.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8878,8 +13637,8 @@ public class TLRPC { public static int constructor = 0xc4f9186b; - public Class responseClass () { - return TL_config.class; + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_config.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8891,8 +13650,8 @@ public class TLRPC { public static int constructor = 0x1fb33026; - public Class responseClass () { - return TL_nearestDc.class; + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_nearestDc.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8908,15 +13667,8 @@ public class TLRPC { public String app_version; public String lang_code; - public Class responseClass () { - return help_AppUpdate.class; - } - - public void readParams(AbsSerializedData stream) { - device_model = stream.readString(); - system_version = stream.readString(); - app_version = stream.readString(); - lang_code = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return help_AppUpdate.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8933,16 +13685,8 @@ public class TLRPC { public ArrayList events = new ArrayList<>(); - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - events.add((TL_inputAppEvent)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8950,8 +13694,8 @@ public class TLRPC { stream.writeInt32(0x1cb5c415); int count = events.size(); stream.writeInt32(count); - for (TL_inputAppEvent event : events) { - event.serializeToStream(stream); + for (int a = 0; a < count; a++) { + events.get(a).serializeToStream(stream); } } } @@ -8961,12 +13705,8 @@ public class TLRPC { public String lang_code; - public Class responseClass () { - return TL_help_inviteText.class; - } - - public void readParams(AbsSerializedData stream) { - lang_code = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_help_inviteText.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -8983,15 +13723,8 @@ public class TLRPC { public int max_id; public int limit; - public Class responseClass () { - return photos_Photos.class; - } - - public void readParams(AbsSerializedData stream) { - user_id = (InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - offset = stream.readInt32(); - max_id = stream.readInt32(); - limit = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return photos_Photos.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9010,14 +13743,8 @@ public class TLRPC { public int id; public long random_id; - public Class responseClass () { - return Updates.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - id = stream.readInt32(); - random_id = stream.readInt64(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9036,23 +13763,8 @@ public class TLRPC { public String message; public InputMedia media; - public Class responseClass () { - return Updates.class; - } - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - contacts.add((InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - stream.readInt32(); - count = stream.readInt32(); - for (int a = 0; a < count; a++) { - random_id.add(stream.readInt64()); - } - message = stream.readString(); - media = (InputMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9081,14 +13793,8 @@ public class TLRPC { public int radius; public int limit; - public Class responseClass () { - return TL_geochats_located.class; - } - - public void readParams(AbsSerializedData stream) { - geo_point = (InputGeoPoint)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - radius = stream.readInt32(); - limit = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_geochats_located.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9105,13 +13811,8 @@ public class TLRPC { public int offset; public int limit; - public Class responseClass () { - return geochats_Messages.class; - } - - public void readParams(AbsSerializedData stream) { - offset = stream.readInt32(); - limit = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return geochats_Messages.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9126,12 +13827,8 @@ public class TLRPC { public TL_inputGeoChat peer; - public Class responseClass () { - return TL_geochats_statedMessage.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_geochats_statedMessage.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9145,12 +13842,8 @@ public class TLRPC { public TL_inputGeoChat peer; - public Class responseClass() { - return TL_messages_chatFull.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_messages_chatFull.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9166,14 +13859,8 @@ public class TLRPC { public String title; public String address; - public Class responseClass () { - return TL_geochats_statedMessage.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - title = stream.readString(); - address = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_geochats_statedMessage.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9190,13 +13877,8 @@ public class TLRPC { public TL_inputGeoChat peer; public InputChatPhoto photo; - public Class responseClass () { - return TL_geochats_statedMessage.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - photo = (InputChatPhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_geochats_statedMessage.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9218,19 +13900,8 @@ public class TLRPC { public int max_id; public int limit; - public Class responseClass () { - return geochats_Messages.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - q = stream.readString(); - filter = (MessagesFilter)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - min_date = stream.readInt32(); - max_date = stream.readInt32(); - offset = stream.readInt32(); - max_id = stream.readInt32(); - limit = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return geochats_Messages.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9254,15 +13925,8 @@ public class TLRPC { public int max_id; public int limit; - public Class responseClass () { - return geochats_Messages.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - offset = stream.readInt32(); - max_id = stream.readInt32(); - limit = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return geochats_Messages.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9280,13 +13944,8 @@ public class TLRPC { public TL_inputGeoChat peer; public boolean typing; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - typing = stream.readBool(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9303,14 +13962,8 @@ public class TLRPC { public String message; public long random_id; - public Class responseClass () { - return TL_geochats_statedMessage.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - message = stream.readString(); - random_id = stream.readInt64(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_geochats_statedMessage.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9328,14 +13981,8 @@ public class TLRPC { public InputMedia media; public long random_id; - public Class responseClass () { - return TL_geochats_statedMessage.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - media = (InputMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - random_id = stream.readInt64(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_geochats_statedMessage.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9354,15 +14001,8 @@ public class TLRPC { public String address; public String venue; - public Class responseClass () { - return TL_geochats_statedMessage.class; - } - - public void readParams(AbsSerializedData stream) { - title = stream.readString(); - geo_point = (InputGeoPoint)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - address = stream.readString(); - venue = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_geochats_statedMessage.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9380,13 +14020,8 @@ public class TLRPC { public int version; public int random_length; - public Class responseClass () { - return messages_DhConfig.class; - } - - public void readParams(AbsSerializedData stream) { - version = stream.readInt32(); - random_length = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return messages_DhConfig.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9403,14 +14038,8 @@ public class TLRPC { public int random_id; public byte[] g_a; - public Class responseClass () { - return EncryptedChat.class; - } - - public void readParams(AbsSerializedData stream) { - user_id = (InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - random_id = stream.readInt32(); - g_a = stream.readByteArray(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return EncryptedChat.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9428,14 +14057,8 @@ public class TLRPC { public byte[] g_b; public long key_fingerprint; - public Class responseClass () { - return EncryptedChat.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputEncryptedChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - g_b = stream.readByteArray(); - key_fingerprint = stream.readInt64(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return EncryptedChat.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9451,12 +14074,8 @@ public class TLRPC { public int chat_id; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - chat_id = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9471,13 +14090,8 @@ public class TLRPC { public TL_inputEncryptedChat peer; public boolean typing; - public Class responseClass() { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputEncryptedChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - typing = stream.readBool(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9493,13 +14107,8 @@ public class TLRPC { public TL_inputEncryptedChat peer; public int max_date; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - peer = (TL_inputEncryptedChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - max_date = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9509,21 +14118,63 @@ public class TLRPC { } } - public static class TL_messages_deleteMessages extends TLObject { - public static int constructor = 0xa5f18925; + public static class TL_messages_receivedQueue extends TLObject { + public static int constructor = 0x55a5bb66; + + public int max_qts; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + Vector vector = new Vector(); + int size = stream.readInt32(exception); + for (int a = 0; a < size; a++) { + vector.objects.add(stream.readInt64(exception)); + } + return vector; + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(max_qts); + } + } + + public static class TL_help_getSupport extends TLObject { + public static int constructor = 0x9cdf08cd; + + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_help_support.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_auth_sendSms extends TLObject { + public static int constructor = 0xda9f3e8; + + public String phone_number; + public String phone_code_hash; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(phone_number); + stream.writeString(phone_code_hash); + } + } + + public static class TL_messages_readMessageContents extends TLObject { + public static int constructor = 0x36a73f77; public ArrayList id = new ArrayList<>(); - public Class responseClass () { - return TL_messages_affectedMessages.class; - } - - public void readParams(AbsSerializedData stream) { - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - id.add(stream.readInt32()); - } + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_messages_affectedMessages.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9537,12 +14188,278 @@ public class TLRPC { } } + public static class TL_account_checkUsername extends TLObject { + public static int constructor = 0x2714d86c; + + public String username; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(username); + } + } + + public static class TL_account_updateUsername extends TLObject { + public static int constructor = 0x3e0bdd7c; + + public String username; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return User.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(username); + } + } + + public static class TL_contacts_search extends TLObject { + public static int constructor = 0x11f812d8; + + public String q; + public int limit; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_contacts_found.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(q); + stream.writeInt32(limit); + } + } + + public static class TL_account_getPrivacy extends TLObject { + public static int constructor = 0xdadbc950; + + public TL_inputPrivacyKeyStatusTimestamp key; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_account_privacyRules.TLdeserialize(stream, constructor, exception); + } + + 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 rules = new ArrayList<>(); + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_account_privacyRules.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + key.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = rules.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + rules.get(a).serializeToStream(stream); + } + } + } + + public static class TL_account_deleteAccount extends TLObject { + public static int constructor = 0x418d4e0b; + + public String reason; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + 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 TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_accountDaysTTL.TLdeserialize(stream, constructor, exception); + } + + 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 TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + ttl.serializeToStream(stream); + } + } + + public static class TL_contacts_resolveUsername extends TLObject { + public static int constructor = 0xbf0131c; + + public String username; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return User.TLdeserialize(stream, constructor, exception); + } + + 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 TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_account_sentChangePhoneCode.TLdeserialize(stream, constructor, exception); + } + + 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 TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return User.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(phone_number); + stream.writeString(phone_code_hash); + stream.writeString(phone_code); + } + } + + public static class TL_messages_getStickers extends TLObject { + public static int constructor = 0xae22e045; + + public String emoticon; + public String hash; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return messages_Stickers.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(emoticon); + stream.writeString(hash); + } + } + + public static class TL_messages_getAllStickers extends TLObject { + public static int constructor = 0xaa3bc868; + + public String hash; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return messages_AllStickers.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(hash); + } + } + + public static class TL_account_updateDeviceLocked extends TLObject { + public static int constructor = 0x38df3532; + + public int period; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(period); + } + } + + public static class TL_messages_getWebPagePreview extends TLObject { + public static int constructor = 0x25223e24; + + public String message; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return MessageMedia.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(message); + } + } + + public static class TL_account_getAuthorizations extends TLObject { + public static int constructor = 0xe320c158; + + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_account_authorizations.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_account_resetAuthorization extends TLObject { + public static int constructor = 0xdf77f3bc; + + public long hash; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(hash); + } + } + public static class TL_account_getPassword extends TLObject { public static int constructor = 0x548a30f5; - public Class responseClass () { - return account_Password.class; + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return account_Password.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9555,12 +14472,8 @@ public class TLRPC { public byte[] current_password_hash; - public Class responseClass () { - return TL_account_passwordSettings.class; - } - - public void readParams(AbsSerializedData stream) { - current_password_hash = stream.readByteArray(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_account_passwordSettings.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9575,13 +14488,8 @@ public class TLRPC { public byte[] current_password_hash; public TL_account_passwordInputSettings new_settings; - public Class responseClass () { - return Bool.class; - } - - public void readParams(AbsSerializedData stream) { - current_password_hash = stream.readByteArray(); - new_settings = (TL_account_passwordInputSettings)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9596,12 +14504,8 @@ public class TLRPC { public byte[] password_hash; - public Class responseClass () { - return TL_auth_authorization.class; - } - - public void readParams(AbsSerializedData stream) { - password_hash = stream.readByteArray(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_auth_authorization.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9614,8 +14518,8 @@ public class TLRPC { public static int constructor = 0xd897bc66; - public Class responseClass () { - return TL_auth_passwordRecovery.class; + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_auth_passwordRecovery.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9628,12 +14532,8 @@ public class TLRPC { public String code; - public Class responseClass () { - return TL_auth_authorization.class; - } - - public void readParams(AbsSerializedData stream) { - code = stream.readString(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_auth_authorization.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { @@ -9642,769 +14542,247 @@ public class TLRPC { } } + public static class TL_messages_exportChatInvite extends TLObject { + public static int constructor = 0x7d885289; + + public int chat_id; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return ExportedChatInvite.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(chat_id); + } + } + + public static class TL_messages_checkChatInvite extends TLObject { + public static int constructor = 0x3eadb1bb; + + public String hash; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return ChatInvite.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(hash); + } + } + + public static class TL_messages_importChatInvite extends TLObject { + public static int constructor = 0x6c50051c; + + public String hash; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(hash); + } + } + + public static class TL_messages_getStickerSet extends TLObject { + public static int constructor = 0x2619a90e; + + public InputStickerSet stickerset; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_messages_stickerSet.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stickerset.serializeToStream(stream); + } + } + + public static class TL_messages_installStickerSet extends TLObject { + public static int constructor = 0xefbbfae9; + + public InputStickerSet stickerset; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stickerset.serializeToStream(stream); + } + } + + public static class TL_messages_uninstallStickerSet extends TLObject { + public static int constructor = 0xf96e55de; + + public InputStickerSet stickerset; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stickerset.serializeToStream(stream); + } + } + //manually created - public static class TL_documentAttributeSticker_old extends TL_documentAttributeSticker { - public static int constructor = 0xfb0a5727; - - public void readParams(AbsSerializedData stream) { - - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_messageMediaUnsupported_old extends TL_messageMediaUnsupported { - public static int constructor = 0x29632a36; - - - public void readParams(AbsSerializedData stream) { - bytes = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeByteArray(bytes); - } - } - - public static class TL_config_old extends TL_config { - public static int constructor = 0x2e54dd74; - - public void readParams(AbsSerializedData stream) { - date = stream.readInt32(); - test_mode = stream.readBool(); - this_dc = stream.readInt32(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - dc_options.add((TL_dcOption) TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); - } - chat_size_max = stream.readInt32(); - broadcast_size_max = stream.readInt32(); - expires = (int) (System.currentTimeMillis() / 1000) + 3600; - chat_big_size = 10; - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(date); - stream.writeBool(test_mode); - stream.writeInt32(this_dc); - stream.writeInt32(0x1cb5c415); - int count = dc_options.size(); - stream.writeInt32(count); - for (TL_dcOption dc_option : dc_options) { - dc_option.serializeToStream(stream); - } - stream.writeInt32(chat_size_max); - stream.writeInt32(broadcast_size_max); - } - } - - public static class TL_document_old extends TL_document { - public static int constructor = 0x9efc6326; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - stream.readInt32(); - date = stream.readInt32(); - TL_documentAttributeFilename fileName = new TL_documentAttributeFilename(); - fileName.file_name = stream.readString(); - attributes.add(fileName); - mime_type = stream.readString(); - size = stream.readInt32(); - thumb = (PhotoSize)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - dc_id = stream.readInt32(); - } - } - - public static class TL_decryptedMessageHolder extends TLObject { - public static int constructor = 0x555555F9; - - public long random_id; + //EncryptedChat start + public static class EncryptedChat extends TLObject { + public int id; + public long access_hash; 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; - - public TL_inputEncryptedChat peer; - public long random_id; - public ByteBufferDesc data; - - public Class responseClass () { - return messages_SentEncryptedMessage.class; - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - peer.serializeToStream(stream); - stream.writeInt64(random_id); - stream.writeByteBuffer(data); - } - - @Override - public void freeResources() { - if (disableFree) { - return; - } - if (data != null) { - BuffersStorage.getInstance().reuseFreeBuffer(data); - data = null; - } - } - } - - public static class TL_userDeleted_old extends TL_userDeleted { - public static int constructor = 0xb29ad7cc; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - first_name = stream.readString(); - last_name = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(first_name); - stream.writeString(last_name); - } - } - - public static class TL_userForeign_old extends TL_userForeign { - public static int constructor = 0x5214c89d; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - first_name = stream.readString(); - last_name = stream.readString(); - access_hash = stream.readInt64(); - photo = (UserProfilePhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeInt64(access_hash); - photo.serializeToStream(stream); - status.serializeToStream(stream); - } - } - - public static class TL_userRequest_old extends TL_userRequest { - public static int constructor = 0x22e8ceb0; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - first_name = stream.readString(); - last_name = stream.readString(); - access_hash = stream.readInt64(); - phone = stream.readString(); - photo = (UserProfilePhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeInt64(access_hash); - stream.writeString(phone); - photo.serializeToStream(stream); - status.serializeToStream(stream); - } - } - - public static class TL_userContact_old extends TL_userContact { - public static int constructor = 0xf2fb8319; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - first_name = stream.readString(); - last_name = stream.readString(); - access_hash = stream.readInt64(); - phone = stream.readString(); - photo = (UserProfilePhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeInt64(access_hash); - stream.writeString(phone); - photo.serializeToStream(stream); - status.serializeToStream(stream); - } - } - - public static class TL_userSelf_old2 extends TL_userSelf { - public static int constructor = 0x7007b451; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - first_name = stream.readString(); - last_name = stream.readString(); - username = stream.readString(); - phone = stream.readString(); - photo = (UserProfilePhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - inactive = stream.readBool(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeString(username); - stream.writeString(phone); - photo.serializeToStream(stream); - status.serializeToStream(stream); - stream.writeBool(inactive); - } - } - - public static class TL_userSelf_old extends TL_userSelf { - public static int constructor = 0x720535ec; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - first_name = stream.readString(); - last_name = stream.readString(); - phone = stream.readString(); - photo = (UserProfilePhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - inactive = stream.readBool(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(id); - stream.writeString(first_name); - stream.writeString(last_name); - stream.writeString(phone); - photo.serializeToStream(stream); - status.serializeToStream(stream); - stream.writeBool(inactive); - } - } - - public static class TL_set_client_DH_params extends TLObject { - public static int constructor = 0xf5045f1f; - + public int admin_id; + public int participant_id; + public byte[] g_a; public byte[] nonce; - public byte[] server_nonce; - public ByteBufferDesc encrypted_data; + public byte[] g_a_or_b; + public long key_fingerprint; + public byte[] a_or_b; //custom + public byte[] auth_key; //custom + public int user_id; //custom + public int ttl; //custom + public int layer; //custom + public int seq_in; //custom + public int seq_out; //custom + public byte[] key_hash; //custom + public short key_use_count_in; //custom + public short key_use_count_out; //custom + public long exchange_id; //custom + public int key_create_date; //custom + public long future_key_fingerprint; //custom + public byte[] future_auth_key; //custom - public Class responseClass () { - return Set_client_DH_params_answer.class; - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeRaw(nonce); - stream.writeRaw(server_nonce); - stream.writeByteBuffer(encrypted_data); - } - - @Override - public void freeResources() { - if (disableFree) { - return; + public static EncryptedChat TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + EncryptedChat result = null; + switch(constructor) { + case 0xfda9a7b7: + result = new TL_encryptedChatRequested_old(); + break; + case 0xc878527e: + result = new TL_encryptedChatRequested(); + break; + case 0xfa56ce36: + result = new TL_encryptedChat(); + break; + case 0x6601d14f: + result = new TL_encryptedChat_old(); + break; + case 0xab7ec0a0: + result = new TL_encryptedChatEmpty(); + break; + case 0x3bf703dc: + result = new TL_encryptedChatWaiting(); + break; + case 0x13d6dd27: + result = new TL_encryptedChatDiscarded(); + break; } - if (encrypted_data != null) { - BuffersStorage.getInstance().reuseFreeBuffer(encrypted_data); - encrypted_data = null; - } - } - } - - public static class TL_messages_sendEncrypted extends TLObject { - public static int constructor = 0xa9776773; - - public TL_inputEncryptedChat peer; - public long random_id; - public ByteBufferDesc data; - - public Class responseClass () { - return messages_SentEncryptedMessage.class; - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - peer.serializeToStream(stream); - stream.writeInt64(random_id); - stream.writeByteBuffer(data); - } - - @Override - public void freeResources() { - if (disableFree) { - return; - } - if (data != null) { - BuffersStorage.getInstance().reuseFreeBuffer(data); - data = null; - } - } - } - - public static class TL_decryptedMessageService_old extends TL_decryptedMessageService { - public static int constructor = 0xaa48327d; - - - public void readParams(AbsSerializedData stream) { - random_id = stream.readInt64(); - random_bytes = stream.readByteArray(); - action = (DecryptedMessageAction)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(random_id); - stream.writeByteArray(random_bytes); - action.serializeToStream(stream); - } - } - - public static class TL_decryptedMessage_old extends TL_decryptedMessage { - public static int constructor = 0x1f814f1f; - - - public void readParams(AbsSerializedData stream) { - random_id = stream.readInt64(); - random_bytes = stream.readByteArray(); - message = stream.readString(); - media = (DecryptedMessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(random_id); - stream.writeByteArray(random_bytes); - stream.writeString(message); - media.serializeToStream(stream); - } - } - - public static class TL_messages_sendEncryptedFile extends TLObject { - public static int constructor = 0x9a901b66; - - public TL_inputEncryptedChat peer; - public long random_id; - public ByteBufferDesc data; - public InputEncryptedFile file; - - public Class responseClass () { - return messages_SentEncryptedMessage.class; - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - peer.serializeToStream(stream); - stream.writeInt64(random_id); - stream.writeByteBuffer(data); - file.serializeToStream(stream); - } - - @Override - public void freeResources() { - if (disableFree) { - return; - } - if (data != null) { - BuffersStorage.getInstance().reuseFreeBuffer(data); - data = null; - } - } - } - - public static class UserStatus extends TLObject { - 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; - - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - 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; - - - public void readParams(AbsSerializedData stream) { - expires = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(expires); - } - } - - 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; - - - public void readParams(AbsSerializedData stream) { - expires = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(expires); - } - } - - public static class TL_upload_file extends TLObject { - public static int constructor = 0x96a18d5; - - public storage_FileType type; - public int mtime; - public ByteBufferDesc bytes; - - public void readParams(AbsSerializedData stream) { - type = (storage_FileType)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - mtime = stream.readInt32(); - bytes = stream.readByteBuffer(); - } - - @Override - public void freeResources() { - if (disableFree) { - return; - } - if (bytes != null) { - BuffersStorage.getInstance().reuseFreeBuffer(bytes); - bytes = null; - } - } - } - - public static class TL_messages_receivedQueue extends TLObject { - public static int constructor = 0x55a5bb66; - - public int max_qts; - - 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(data.readInt64()); - } - } - - public void readParams(AbsSerializedData stream) { - max_qts = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(max_qts); - } - } - - public static class TL_account_getWallPapers extends TLObject { - public static int constructor = 0xc04cfac2; - - public Class responseClass () { - return Vector.class; - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - - 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 static class TL_get_future_salts extends TLObject { - public static int constructor = 0xb921bd04; - - public int num; - - public int layer () { - return 0; - } - - public Class responseClass () { - return TL_futuresalts.class; - } - - public void readParams(AbsSerializedData stream) { - num = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(num); - } - } - - public static class TL_rpc_drop_answer extends TLObject { - public static int constructor = 0x58e4a740; - - public long req_msg_id; - - public int layer () { - return 0; - } - - public Class responseClass() { - return RpcDropAnswer.class; - } - - public void readParams(AbsSerializedData stream) { - req_msg_id = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(req_msg_id); - } - } - - public static class TL_msg_container extends TLObject { - public ArrayList messages; - - public static int constructor = 0x73f1f8dc; - - public void readParams(AbsSerializedData stream) { - messages = new ArrayList<>(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - TL_protoMessage message = new TL_protoMessage(); - message.msg_id = stream.readInt64(); - message.seqno = stream.readInt32(); - message.bytes = stream.readInt32(); - int constructor = stream.readInt32(); - TLObject request = ConnectionsManager.getInstance().getRequestWithMessageId(message.msg_id); - message.body = TLClassStore.Instance().TLdeserialize(stream, constructor, request); - messages.add(message); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(messages.size()); - for (TLObject obj : messages) { - TL_protoMessage proto = (TL_protoMessage)obj; - stream.writeInt64(proto.msg_id); - stream.writeInt32(proto.seqno); - stream.writeInt32(proto.bytes); - proto.body.serializeToStream(stream); - } - } - } - - public static class TL_rpc_result extends TLObject { - public static int constructor = 0xf35c6d01; - - public long req_msg_id; - public TLObject result; - - public void readParams(AbsSerializedData stream) { - req_msg_id = stream.readInt64(); - TLObject request = ConnectionsManager.getInstance().getRequestWithMessageId(req_msg_id); - result = TLClassStore.Instance().TLdeserialize(stream, stream.readInt32(), request); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(req_msg_id); - result.serializeToStream(stream); - } - - @Override - public void freeResources() { - if (disableFree) { - return; + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in EncryptedChat", constructor)); } if (result != null) { - result.freeResources(); + result.readParams(stream, exception); } + return result; } } + //EncryptedChat end - public static class TL_futuresalts extends TLObject { - public static int constructor = 0xae500895; - - public long req_msg_id; - public int now; - public ArrayList salts = new ArrayList<>(); - - public void readParams(AbsSerializedData stream) { - req_msg_id = stream.readInt64(); - now = stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - TL_futureSalt salt = new TL_futureSalt(); - salt.readParams(stream); - salts.add(salt); - } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(req_msg_id); - stream.writeInt32(now); - int count = salts.size(); - stream.writeInt32(count); - for (TL_futureSalt salt : salts) { - salt.serializeToStream(stream); - } - } - } - - public static class TL_gzip_packed extends TLObject { - public static int constructor = 0x3072cfa1; - - public byte[] packed_data; - - public void readParams(AbsSerializedData stream) { - packed_data = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeByteArray(packed_data); - } - } - + //Message start public static class Message extends TLObject { - public int flags; public int id; - public int fwd_from_id; - public int fwd_date; public int from_id; public Peer to_id; public int date; + public MessageAction action; + public int fwd_from_id; + public int fwd_date; + public int reply_to_msg_id; public String message; public MessageMedia media; - public int reply_to_msg_id; - public MessageAction action; - public int send_state = 0; - public int fwd_msg_id = 0; - public String attachPath = ""; - public long random_id; - public int local_id = 0; - public long dialog_id; - public int ttl; - public int destroyTime; - public int layer; - public int seq_in; - public int seq_out; - public TLRPC.Message replyMessage; - public VideoEditedInfo videoEditedInfo = null; + public int flags; + public int send_state = 0; //custom + public int fwd_msg_id = 0; //custom + public String attachPath = ""; //custom + public long random_id; //custom + public int local_id = 0; //custom + public long dialog_id; //custom + public int ttl; //custom + public int destroyTime; //custom + public int layer; //custom + public int seq_in; //custom + public int seq_out; //custom + public TLRPC.Message replyMessage; //custom + + public static Message TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + Message result = null; + switch(constructor) { + case 0x1d86f70e: + result = new TL_messageService(); + break; + case 0xa7ab1991: + result = new TL_message(); + break; + case 0x83e5de54: + result = new TL_messageEmpty(); + break; + case 0xa367e716: + result = new TL_messageForwarded_old2(); //custom + break; + case 0x5f46804: + result = new TL_messageForwarded_old(); //custom + break; + case 0x567699b3: + result = new TL_message_old2(); //custom + break; + case 0x9f8d60bb: + result = new TL_messageService_old(); //custom + break; + case 0x22eb6aba: + result = new TL_message_old(); //custom + break; + case 0x555555F8: + result = new TL_message_secret(); //custom + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in Message", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } } public static class TL_messageForwarded_old2 extends Message { public static int constructor = 0xa367e716; - public void readParams(AbsSerializedData stream) { - flags = stream.readInt32(); - id = stream.readInt32(); - fwd_from_id = stream.readInt32(); - fwd_date = stream.readInt32(); - from_id = stream.readInt32(); - to_id = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - date = stream.readInt32(); - message = stream.readString(); + public void readParams(AbsSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + id = stream.readInt32(exception); + fwd_from_id = stream.readInt32(exception); + fwd_date = stream.readInt32(exception); + from_id = stream.readInt32(exception); + to_id = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + date = stream.readInt32(exception); + message = stream.readString(exception); flags |= MESSAGE_FLAG_FWD; - media = (MessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + media = MessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); if (id < 0) { - fwd_msg_id = stream.readInt32(); + fwd_msg_id = stream.readInt32(exception); } if (id < 0 || (media != null && !(media instanceof TL_messageMediaEmpty) && message != null && message.length() != 0 && message.startsWith("-1"))) { - attachPath = stream.readString(); - } - if (id < 0 && message.length() > 6 && media instanceof TL_messageMediaVideo) { - videoEditedInfo = new VideoEditedInfo(); - videoEditedInfo.parseString(message); + attachPath = stream.readString(exception); } } @@ -10429,31 +14807,28 @@ public class TLRPC { public static class TL_message extends Message { public static int constructor = 0xa7ab1991; - - public void readParams(AbsSerializedData stream) { - flags = stream.readInt32(); - id = stream.readInt32(); - from_id = stream.readInt32(); - to_id = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - if ((flags & MESSAGE_FLAG_FWD) != 0) { - fwd_from_id = stream.readInt32(); - fwd_date = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + id = stream.readInt32(exception); + from_id = stream.readInt32(exception); + to_id = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + if ((flags & 4) != 0) { + fwd_from_id = stream.readInt32(exception); } - if ((flags & MESSAGE_FLAG_REPLY) != 0) { - reply_to_msg_id = stream.readInt32(); + if ((flags & 4) != 0) { + fwd_date = stream.readInt32(exception); } - date = stream.readInt32(); - message = stream.readString(); - media = (MessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + if ((flags & 8) != 0) { + reply_to_msg_id = stream.readInt32(exception); + } + date = stream.readInt32(exception); + message = stream.readString(exception); + media = MessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); if (id < 0 || (media != null && !(media instanceof TL_messageMediaEmpty) && message != null && message.length() != 0 && message.startsWith("-1"))) { - attachPath = stream.readString(); - } - if (id < 0 && message.length() > 6 && media instanceof TL_messageMediaVideo) { - videoEditedInfo = new VideoEditedInfo(); - videoEditedInfo.parseString(message); + attachPath = stream.readString(exception); } if ((flags & MESSAGE_FLAG_FWD) != 0 && id < 0) { - fwd_msg_id = stream.readInt32(); + fwd_msg_id = stream.readInt32(exception); } } @@ -10463,11 +14838,13 @@ public class TLRPC { stream.writeInt32(id); stream.writeInt32(from_id); to_id.serializeToStream(stream); - if ((flags & MESSAGE_FLAG_FWD) != 0) { + if ((flags & 4) != 0) { stream.writeInt32(fwd_from_id); + } + if ((flags & 4) != 0) { stream.writeInt32(fwd_date); } - if ((flags & MESSAGE_FLAG_REPLY) != 0) { + if ((flags & 8) != 0) { stream.writeInt32(reply_to_msg_id); } stream.writeInt32(date); @@ -10484,20 +14861,16 @@ public class TLRPC { public static int constructor = 0x567699b3; - public void readParams(AbsSerializedData stream) { - flags = stream.readInt32(); - id = stream.readInt32(); - from_id = stream.readInt32(); - to_id = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - date = stream.readInt32(); - message = stream.readString(); - media = (MessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public void readParams(AbsSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + id = stream.readInt32(exception); + from_id = stream.readInt32(exception); + to_id = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + date = stream.readInt32(exception); + message = stream.readString(exception); + media = MessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); if (id < 0 || (media != null && !(media instanceof TL_messageMediaEmpty) && message != null && message.length() != 0 && message.startsWith("-1"))) { - attachPath = stream.readString(); - } - if (id < 0 && message.length() > 6 && media instanceof TL_messageMediaVideo) { - videoEditedInfo = new VideoEditedInfo(); - videoEditedInfo.parseString(message); + attachPath = stream.readString(exception); } } @@ -10514,42 +14887,18 @@ public class TLRPC { } } - public static class TL_messageService extends Message { - public static int constructor = 0x1d86f70e; - - - public void readParams(AbsSerializedData stream) { - flags = stream.readInt32(); - id = stream.readInt32(); - from_id = stream.readInt32(); - to_id = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - date = stream.readInt32(); - action = (MessageAction)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(flags); - stream.writeInt32(id); - stream.writeInt32(from_id); - to_id.serializeToStream(stream); - stream.writeInt32(date); - action.serializeToStream(stream); - } - } - public static class TL_messageService_old extends TL_messageService { public static int constructor = 0x9f8d60bb; - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - from_id = stream.readInt32(); - to_id = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - flags |= stream.readBool() ? MESSAGE_FLAG_OUT : 0; - flags |= stream.readBool() ? MESSAGE_FLAG_UNREAD : 0; - date = stream.readInt32(); - action = (MessageAction)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + from_id = stream.readInt32(exception); + to_id = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + flags |= stream.readBool(exception) ? MESSAGE_FLAG_OUT : 0; + flags |= stream.readBool(exception) ? MESSAGE_FLAG_UNREAD : 0; + date = stream.readInt32(exception); + action = MessageAction.TLdeserialize(stream, stream.readInt32(exception), exception); } public void serializeToStream(AbsSerializedData stream) { @@ -10568,27 +14917,23 @@ public class TLRPC { public static int constructor = 0x5f46804; - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - fwd_from_id = stream.readInt32(); - fwd_date = stream.readInt32(); - from_id = stream.readInt32(); - to_id = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - flags |= stream.readBool() ? MESSAGE_FLAG_OUT : 0; - flags |= stream.readBool() ? MESSAGE_FLAG_UNREAD : 0; + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + fwd_from_id = stream.readInt32(exception); + fwd_date = stream.readInt32(exception); + from_id = stream.readInt32(exception); + to_id = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + flags |= stream.readBool(exception) ? MESSAGE_FLAG_OUT : 0; + flags |= stream.readBool(exception) ? MESSAGE_FLAG_UNREAD : 0; flags |= MESSAGE_FLAG_FWD; - date = stream.readInt32(); - message = stream.readString(); - media = (MessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + date = stream.readInt32(exception); + message = stream.readString(exception); + media = MessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); if (id < 0) { - fwd_msg_id = stream.readInt32(); + fwd_msg_id = stream.readInt32(exception); } if (id < 0 || (media != null && !(media instanceof TL_messageMediaEmpty) && message != null && message.length() != 0 && message.startsWith("-1"))) { - attachPath = stream.readString(); - } - if (id < 0 && message.length() > 6 && media instanceof TL_messageMediaVideo) { - videoEditedInfo = new VideoEditedInfo(); - videoEditedInfo.parseString(message); + attachPath = stream.readString(exception); } } @@ -10614,21 +14959,17 @@ public class TLRPC { public static class TL_message_old extends TL_message { public static int constructor = 0x22eb6aba; - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - from_id = stream.readInt32(); - to_id = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - flags |= stream.readBool() ? MESSAGE_FLAG_OUT : 0; - flags |= stream.readBool() ? MESSAGE_FLAG_UNREAD : 0; - date = stream.readInt32(); - message = stream.readString(); - media = (MessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + from_id = stream.readInt32(exception); + to_id = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + flags |= stream.readBool(exception) ? MESSAGE_FLAG_OUT : 0; + flags |= stream.readBool(exception) ? MESSAGE_FLAG_UNREAD : 0; + date = stream.readInt32(exception); + message = stream.readString(exception); + media = MessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); if (id < 0 || (media != null && !(media instanceof TL_messageMediaEmpty) && message != null && message.length() != 0 && message.startsWith("-1"))) { - attachPath = stream.readString(); - } - if (id < 0 && message.length() > 6 && media instanceof TL_messageMediaVideo) { - videoEditedInfo = new VideoEditedInfo(); - videoEditedInfo.parseString(message); + attachPath = stream.readString(exception); } } @@ -10649,21 +14990,17 @@ public class TLRPC { public static class TL_message_secret extends TL_message { public static int constructor = 0x555555F8; - public void readParams(AbsSerializedData stream) { - flags = stream.readInt32(); - id = stream.readInt32(); - ttl = stream.readInt32(); - from_id = stream.readInt32(); - to_id = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - date = stream.readInt32(); - message = stream.readString(); - media = (MessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public void readParams(AbsSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + id = stream.readInt32(exception); + ttl = stream.readInt32(exception); + from_id = stream.readInt32(exception); + to_id = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + date = stream.readInt32(exception); + message = stream.readString(exception); + media = MessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); if (id < 0 || (media != null && !(media instanceof TL_messageMediaEmpty) && message != null && message.length() != 0 && message.startsWith("-1"))) { - attachPath = stream.readString(); - } - if (id < 0 && message.length() > 6 && media instanceof TL_messageMediaVideo) { - videoEditedInfo = new VideoEditedInfo(); - videoEditedInfo.parseString(message); + attachPath = stream.readString(exception); } } @@ -10680,30 +15017,60 @@ public class TLRPC { stream.writeString(attachPath); } } + //Message end - public static class Vector extends TLObject { - public static int constructor = 0x1cb5c415; - public ArrayList objects = new ArrayList<>(); - } - - public static class User extends TLObject { - public int id; - public String first_name; - public String last_name; - public String username; - public long access_hash; - public String phone; - public UserProfilePhoto photo; - public UserStatus status; - public boolean inactive; + //TL_dialog start + public static class TL_dialog extends TLObject { + public static int constructor = 0xc1dd804a; + + public Peer peer; + public int top_message; + public int read_inbox_max_id; + public int unread_count; + public PeerNotifySettings notify_settings; + public int last_message_date; //custom + public long id; //custom + public int last_read; //custom + + public static TL_dialog TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_dialog.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_dialog", constructor)); + } else { + return null; + } + } + TL_dialog result = new TL_dialog(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + peer = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + top_message = stream.readInt32(exception); + read_inbox_max_id = stream.readInt32(exception); + unread_count = stream.readInt32(exception); + notify_settings = PeerNotifySettings.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + peer.serializeToStream(stream); + stream.writeInt32(top_message); + stream.writeInt32(read_inbox_max_id); + stream.writeInt32(unread_count); + notify_settings.serializeToStream(stream); + } } + //TL_dialog end + //User start public static class TL_userEmpty extends User { public static int constructor = 0x200250ba; - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); first_name = "DELETED"; last_name = ""; @@ -10716,13 +15083,15 @@ public class TLRPC { stream.writeInt32(id); } } + //User end + //Chat start public static class TL_chatEmpty extends Chat { public static int constructor = 0x9ba2d800; - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); + public void readParams(AbsSerializedData stream, boolean exception) { + id = stream.readInt32(exception); title = "DELETED"; } @@ -10732,502 +15101,232 @@ public class TLRPC { stream.writeInt32(id); } } + //Chat end - public static class TL_userProfilePhotoOld extends UserProfilePhoto { - public static int constructor = 0x990d1493; + //functions memory optimize + public static class TL_upload_saveFilePart extends TLObject { + public static int constructor = 0xb304a621; + public long file_id; + public int file_part; + public ByteBufferDesc bytes; - public void readParams(AbsSerializedData stream) { - photo_small = (FileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - photo_big = (FileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - photo_small.serializeToStream(stream); - photo_big.serializeToStream(stream); - } - } - - public static class TL_ping extends TLObject { - public static int constructor = 0x7abe77ec; - - public long ping_id; - - public Class responseClass () { - return TL_pong.class; + stream.writeInt64(file_id); + stream.writeInt32(file_part); + stream.writeByteBuffer(bytes); } - public int layer () { - return 0; - } - - public void readParams(AbsSerializedData stream) { - ping_id = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(ping_id); - } - } - - public static class TL_ping_delay_disconnect extends TLObject { - public static int constructor = 0xf3427b8c; - - public long ping_id; - public int disconnect_delay; - - public Class responseClass () { - return TL_pong.class; - } - - public int layer () { - return 0; - } - - public void readParams(AbsSerializedData stream) { - ping_id = stream.readInt64(); - disconnect_delay = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(ping_id); - stream.writeInt32(disconnect_delay); - } - } - - public static class TL_destroy_session extends TLObject { - public static int constructor = 0xe7512126; - - public long session_id; - - public Class responseClass () { - return DestroySessionRes.class; - } - - public int layer () { - return 0; - } - - public void readParams(AbsSerializedData stream) { - session_id = stream.readInt64(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(session_id); - } - } - - public static class TL_destroy_sessions extends TLObject { - public static int constructor = 0xa13dc52f; - - public ArrayList session_ids = new ArrayList<>(); - - public Class responseClass () { - return TL_destroy_sessions_res.class; - } - - public int layer () { - return 0; - } - - public void readParams(AbsSerializedData stream) { - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - session_ids.add(stream.readInt64()); + @Override + public void freeResources() { + if (disableFree) { + return; } - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - int count = session_ids.size(); - stream.writeInt32(count); - for (Long session_id : session_ids) { - stream.writeInt64(session_id); + if (bytes != null) { + BuffersStorage.getInstance().reuseFreeBuffer(bytes); + bytes = null; } } } - public static class TL_dialog extends TLObject { - public static int constructor = 0xc1dd804a; + public static class TL_upload_saveBigFilePart extends TLObject { + public static int constructor = 0xde7b673d; - public Peer peer; - public int top_message; - public int unread_count; - public int read_inbox_max_id; - public PeerNotifySettings notify_settings; - public int last_message_date; - public long id; - public int last_read; + public long file_id; + public int file_part; + public int file_total_parts; + public ByteBufferDesc bytes; - public void readParams(AbsSerializedData stream) { - peer = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - top_message = stream.readInt32(); - read_inbox_max_id = stream.readInt32(); - unread_count = stream.readInt32(); - notify_settings = (PeerNotifySettings)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(file_id); + stream.writeInt32(file_part); + stream.writeInt32(file_total_parts); + stream.writeByteBuffer(bytes); + } + + @Override + public void freeResources() { + if (disableFree) { + return; + } + if (bytes != null) { + BuffersStorage.getInstance().reuseFreeBuffer(bytes); + bytes = null; + } + } + } + + public static class TL_upload_file extends TLObject { + public static int constructor = 0x96a18d5; + + public storage_FileType type; + public int mtime; + public ByteBufferDesc bytes; + + public static TL_upload_file TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_upload_file.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_upload_file", constructor)); + } else { + return null; + } + } + TL_upload_file result = new TL_upload_file(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + type = storage_FileType.TLdeserialize(stream, stream.readInt32(exception), exception); + mtime = stream.readInt32(exception); + bytes = stream.readByteBuffer(exception); + } + + @Override + public void freeResources() { + if (disableFree) { + return; + } + if (bytes != null) { + BuffersStorage.getInstance().reuseFreeBuffer(bytes); + bytes = null; + } + } + } + + public static class TL_messages_sendEncryptedFile extends TLObject { + public static int constructor = 0x9a901b66; + + public TL_inputEncryptedChat peer; + public long random_id; + public ByteBufferDesc data; + public InputEncryptedFile file; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return messages_SentEncryptedMessage.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); - stream.writeInt32(top_message); - stream.writeInt32(read_inbox_max_id); - stream.writeInt32(unread_count); - notify_settings.serializeToStream(stream); - } - } - - public static class EncryptedChat extends TLObject { - public int id; - public long access_hash; - public int date; - public int admin_id; - public int participant_id; - public byte[] g_a_or_b; - public long key_fingerprint; - public byte[] g_a; - public byte[] a_or_b; - public byte[] auth_key; - public int user_id; - public int ttl; - 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 { - public int dc_id; - public long volume_id; - public int local_id; - public long secret; - public String ext; - public byte[] key; - public byte[] iv; - } - - public static class TL_fileEncryptedLocation extends FileLocation { - public static int constructor = 0x55555554; - - - public void readParams(AbsSerializedData stream) { - dc_id = stream.readInt32(); - volume_id = stream.readInt64(); - local_id = stream.readInt32(); - secret = stream.readInt64(); - key = stream.readByteArray(); - iv = stream.readByteArray(); + stream.writeInt64(random_id); + stream.writeByteBuffer(data); + file.serializeToStream(stream); } - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(dc_id); - stream.writeInt64(volume_id); - stream.writeInt32(local_id); - stream.writeInt64(secret); - stream.writeByteArray(key); - stream.writeByteArray(iv); - } - } - - public static class VideoEditedInfo { - public long startTime; - public long endTime; - public int rotationValue; - public int originalWidth; - public int originalHeight; - public int resultWidth; - public int resultHeight; - public int bitrate; - public String originalPath; - - public String getString() { - return String.format(Locale.US, "-1_%d_%d_%d_%d_%d_%d_%d_%d_%s", startTime, endTime, rotationValue, originalWidth, originalHeight, bitrate, resultWidth, resultHeight, originalPath); - } - - public void parseString(String string) { - if (string.length() < 6) { + @Override + public void freeResources() { + if (disableFree) { return; } - String args[] = string.split("_"); - if (args.length >= 10) { - startTime = Long.parseLong(args[1]); - endTime = Long.parseLong(args[2]); - rotationValue = Integer.parseInt(args[3]); - originalWidth = Integer.parseInt(args[4]); - originalHeight = Integer.parseInt(args[5]); - bitrate = Integer.parseInt(args[6]); - resultWidth = Integer.parseInt(args[7]); - resultHeight = Integer.parseInt(args[8]); - for (int a = 9; a < args.length; a++) { - if (originalPath == null) { - originalPath = args[a]; - } else { - originalPath += "_" + args[a]; - } - } + if (data != null) { + BuffersStorage.getInstance().reuseFreeBuffer(data); + data = null; } } } - public static class Video extends TLObject { - public long id; - public long access_hash; - public int user_id; - public int date; - public String caption; - public int duration; - public String mime_type; - public int size; - public PhotoSize thumb; - public int dc_id; - public int w; - public int h; - public byte[] key; - public byte[] iv; - public VideoEditedInfo videoEditedInfo = null; - } + public static class TL_messages_sendEncrypted extends TLObject { + public static int constructor = 0xa9776773; - public static class Document extends TLObject { - public long id; - public long access_hash; - public int date; - public String mime_type; - public int size; - public PhotoSize thumb; - public int dc_id; - public ArrayList attributes = new ArrayList<>(); - public byte[] key; - public byte[] iv; - } + public TL_inputEncryptedChat peer; + public long random_id; + public ByteBufferDesc data; - public static class Audio extends TLObject { - public long id; - public long access_hash; - public int user_id; - public int date; - public int duration; - public String mime_type; - public int size; - public int dc_id; - public byte[] key; - public byte[] iv; - } - - public static class MessageAction extends TLObject { - public Photo photo; - public UserProfilePhoto newUserPhoto; - public int user_id; - public String title; - public ArrayList users = new ArrayList<>(); - public String address; - public int ttl; - public DecryptedMessageAction encryptedAction; - } - - public static class TL_messageActionTTLChange extends MessageAction { - public static int constructor = 0x55555552; - - public void readParams(AbsSerializedData stream) { - ttl = stream.readInt32(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return messages_SentEncryptedMessage.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(ttl); - } - } - - public static class TL_messageActionCreatedBroadcastList extends MessageAction { - public static int constructor = 0x55555557; - - public void readParams(AbsSerializedData stream) { + peer.serializeToStream(stream); + stream.writeInt64(random_id); + stream.writeByteBuffer(data); } - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_documentEncrypted extends TL_document { - public static int constructor = 0x55555558; - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - date = stream.readInt32(); - mime_type = stream.readString(); - size = stream.readInt32(); - thumb = (PhotoSize)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - dc_id = stream.readInt32(); - stream.readInt32(); - int count = stream.readInt32(); - for (int a = 0; a < count; a++) { - attributes.add((DocumentAttribute)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); + @Override + public void freeResources() { + if (disableFree) { + return; } - key = stream.readByteArray(); - iv = stream.readByteArray(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - stream.writeInt32(date); - stream.writeString(mime_type); - stream.writeInt32(size); - thumb.serializeToStream(stream); - stream.writeInt32(dc_id); - stream.writeInt32(0x1cb5c415); - int count = attributes.size(); - stream.writeInt32(count); - for (int a = 0; a < count; a++) { - attributes.get(a).serializeToStream(stream); + if (data != null) { + BuffersStorage.getInstance().reuseFreeBuffer(data); + data = null; } - stream.writeByteArray(key); - stream.writeByteArray(iv); } } - public static class TL_documentEncrypted_old extends TL_document { - public static int constructor = 0x55555556; + public static class TL_messages_sendEncryptedService extends TLObject { + public static int constructor = 0x32d439a4; + public TL_inputEncryptedChat peer; + public long random_id; + public ByteBufferDesc data; - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - stream.readInt32(); - date = stream.readInt32(); - TL_documentAttributeFilename fileName = new TL_documentAttributeFilename(); - fileName.file_name = stream.readString(); - attributes.add(fileName); - mime_type = stream.readString(); - size = stream.readInt32(); - thumb = (PhotoSize)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - dc_id = stream.readInt32(); - key = stream.readByteArray(); - iv = stream.readByteArray(); - } - } - - public static class TL_videoEncrypted extends TL_video { - public static int constructor = 0x55555553; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - user_id = stream.readInt32(); - date = stream.readInt32(); - caption = stream.readString(); - duration = stream.readInt32(); - size = stream.readInt32(); - thumb = (PhotoSize)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - dc_id = stream.readInt32(); - w = stream.readInt32(); - h = stream.readInt32(); - key = stream.readByteArray(); - iv = stream.readByteArray(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return messages_SentEncryptedMessage.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - stream.writeInt32(user_id); - stream.writeInt32(date); - stream.writeString(caption); - stream.writeInt32(duration); - stream.writeInt32(size); - thumb.serializeToStream(stream); - stream.writeInt32(dc_id); - stream.writeInt32(w); - stream.writeInt32(h); - stream.writeByteArray(key); - stream.writeByteArray(iv); + peer.serializeToStream(stream); + stream.writeInt64(random_id); + stream.writeByteBuffer(data); + } + + @Override + public void freeResources() { + if (disableFree) { + return; + } + if (data != null) { + BuffersStorage.getInstance().reuseFreeBuffer(data); + data = null; + } } } - public static class TL_audioEncrypted extends TL_audio { - public static int constructor = 0x555555F6; + public static class TL_set_client_DH_params extends TLObject { + public static int constructor = 0xf5045f1f; + public byte[] nonce; + public byte[] server_nonce; + public ByteBufferDesc encrypted_data; - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - user_id = stream.readInt32(); - date = stream.readInt32(); - duration = stream.readInt32(); - size = stream.readInt32(); - dc_id = stream.readInt32(); - key = stream.readByteArray(); - iv = stream.readByteArray(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return Set_client_DH_params_answer.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - stream.writeInt32(user_id); - stream.writeInt32(date); - stream.writeInt32(duration); - stream.writeInt32(size); - stream.writeInt32(dc_id); - stream.writeByteArray(key); - stream.writeByteArray(iv); - } - } - - public static class TL_messageActionUserUpdatedPhoto extends MessageAction { - public static int constructor = 0x55555551; - - public void readParams(AbsSerializedData stream) { - newUserPhoto = (UserProfilePhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - newUserPhoto.serializeToStream(stream); - } - } - - public static class TL_messageActionUserJoined extends MessageAction { - public static int constructor = 0x55555550; - - public void readParams(AbsSerializedData stream) { - - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_messageActionLoginUnknownLocation extends MessageAction { - public static int constructor = 0x555555F5; - - public void readParams(AbsSerializedData stream) { - title = stream.readString(); - address = stream.readString(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeString(title); - stream.writeString(address); + stream.writeRaw(nonce); + stream.writeRaw(server_nonce); + stream.writeByteBuffer(encrypted_data); + } + + @Override + public void freeResources() { + if (disableFree) { + return; + } + if (encrypted_data != null) { + BuffersStorage.getInstance().reuseFreeBuffer(encrypted_data); + encrypted_data = null; + } } } + //functions public static class invokeWithLayer extends TLObject { public static int constructor = 0xda9b0d0d; @@ -11262,194 +15361,227 @@ public class TLRPC { } } - public static class TL_encryptedChat_old extends TL_encryptedChat { - public static int constructor = 0x6601d14f; + public static class TL_destroy_sessions extends TLObject { + public static int constructor = 0xa13dc52f; + public ArrayList session_ids = new ArrayList<>(); - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - access_hash = stream.readInt64(); - date = stream.readInt32(); - admin_id = stream.readInt32(); - participant_id = stream.readInt32(); - g_a_or_b = stream.readByteArray(); - stream.readByteArray(); - key_fingerprint = stream.readInt64(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_destroy_sessions_res.TLdeserialize(stream, constructor, exception); + } + + public int layer() { + return 0; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + session_ids.add(stream.readInt64(exception)); + } } public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(TL_encryptedChat.constructor); - stream.writeInt32(id); - stream.writeInt64(access_hash); - stream.writeInt32(date); - stream.writeInt32(admin_id); - stream.writeInt32(participant_id); - stream.writeByteArray(g_a_or_b); - stream.writeInt64(key_fingerprint); + stream.writeInt32(constructor); + int count = session_ids.size(); + stream.writeInt32(count); + for (Long session_id : session_ids) { + stream.writeInt64(session_id); + } } } - public static class TL_encryptedChatRequested_old extends EncryptedChat { - public static int constructor = 0xfda9a7b7; + public static class TL_ping extends TLObject { + public static int constructor = 0x7abe77ec; + public long ping_id; - public void readParams(AbsSerializedData stream) { - id = stream.readInt32(); - access_hash = stream.readInt64(); - date = stream.readInt32(); - admin_id = stream.readInt32(); - participant_id = stream.readInt32(); - g_a = stream.readByteArray(); - stream.readByteArray(); + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_pong.TLdeserialize(stream, constructor, exception); + } + + public int layer() { + return 0; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + ping_id = stream.readInt64(exception); } public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(TL_encryptedChatRequested.constructor); - stream.writeInt32(id); - stream.writeInt64(access_hash); - stream.writeInt32(date); - stream.writeInt32(admin_id); - stream.writeInt32(participant_id); - stream.writeByteArray(g_a); + stream.writeInt32(constructor); + stream.writeInt64(ping_id); } } - public static class TL_decryptedMessageActionDeleteMessages extends DecryptedMessageAction { - public static int constructor = 0x65614304; + public static class TL_ping_delay_disconnect extends TLObject { + public static int constructor = 0xf3427b8c; + public long ping_id; + public int disconnect_delay; - public void readParams(AbsSerializedData stream) { - boolean[] error = new boolean[1]; - stream.readInt32(error); - if (error[0]) { - return; - } - int count = stream.readInt32(error); - if (error[0]) { - return; - } - for (long a = 0; a < count; a++) { - random_ids.add(stream.readInt64(error)); - if (error[0]) { - return; + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_pong.TLdeserialize(stream, constructor, exception); + } + + public int layer() { + return 0; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + ping_id = stream.readInt64(exception); + disconnect_delay = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(ping_id); + stream.writeInt32(disconnect_delay); + } + } + + public static class TL_destroy_session extends TLObject { + public static int constructor = 0xe7512126; + + public long session_id; + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return DestroySessionRes.TLdeserialize(stream, constructor, exception); + } + + public int layer() { + return 0; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + session_id = stream.readInt64(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(session_id); + } + } + + public static class TL_invokeAfterMsg extends TLObject { + public static int constructor = 0xcb9f372d; + + public long msg_id; + public TLObject query; + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(msg_id); + query.serializeToStream(stream); + } + } + + public static class TL_get_future_salts extends TLObject { + public static int constructor = 0xb921bd04; + + public int num; + + public int layer() { + return 0; + } + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return TL_futuresalts.TLdeserialize(stream, constructor, exception); + } + + public void readParams(AbsSerializedData stream, boolean exception) { + num = stream.readInt32(exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(num); + } + } + + public static class TL_rpc_drop_answer extends TLObject { + public static int constructor = 0x58e4a740; + + public long req_msg_id; + + public int layer() { + return 0; + } + + public TLObject deserializeResponse(AbsSerializedData stream, int constructor, boolean exception) { + return RpcDropAnswer.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(req_msg_id); + } + } + + public static class TL_msg_container extends TLObject { + public ArrayList messages; + + public static int constructor = 0x73f1f8dc; + + public void readParams(AbsSerializedData stream, boolean exception) { + messages = new ArrayList<>(); + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_protoMessage message = new TL_protoMessage(); + message.msg_id = stream.readInt64(exception); + message.seqno = stream.readInt32(exception); + message.bytes = stream.readInt32(exception); + int position = stream.getPosition(); + message.body = ConnectionsManager.getInstance().deserialize(ConnectionsManager.getInstance().getRequestWithMessageId(message.msg_id), stream, exception); + if (message.body == null) { + stream.skip(message.bytes - (stream.getPosition() - position)); + } else { + messages.add(message); } } } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = random_ids.size(); - stream.writeInt32(count); - for (Long value : random_ids) { - stream.writeInt64(value); + stream.writeInt32(messages.size()); + for (TLObject obj : messages) { + TL_protoMessage proto = (TL_protoMessage) obj; + stream.writeInt64(proto.msg_id); + stream.writeInt32(proto.seqno); + stream.writeInt32(proto.bytes); + proto.body.serializeToStream(stream); } } } - public static class TL_decryptedMessageActionCommitKey extends DecryptedMessageAction { - public static int constructor = 0xec2e0b9b; + public static class TL_rpc_result extends TLObject { + public static int constructor = 0xf35c6d01; + public long req_msg_id; + public TLObject result; - 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; - - - public void readParams(AbsSerializedData stream) { - boolean[] error = new boolean[1]; - stream.readInt32(error); - if (error[0]) { - return; - } - int count = stream.readInt32(error); - if (error[0]) { - return; - } - for (long a = 0; a < count; a++) { - random_ids.add(stream.readInt64(error)); - if (error[0]) { - return; + public static TL_rpc_result TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_rpc_result.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_rpc_result", constructor)); + } else { + return null; } } + TL_rpc_result result = new TL_rpc_result(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbsSerializedData stream, boolean exception) { + req_msg_id = stream.readInt64(exception); + result = ConnectionsManager.getInstance().deserialize(ConnectionsManager.getInstance().getRequestWithMessageId(req_msg_id), stream, exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(0x1cb5c415); - int count = random_ids.size(); - stream.writeInt32(count); - for (Long value : random_ids) { - stream.writeInt64(value); - } - } - } - - public static class TL_messageEncryptedAction extends MessageAction { - public static int constructor = 0x555555F7; - - public void readParams(AbsSerializedData stream) { - encryptedAction = (DecryptedMessageAction)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - encryptedAction.serializeToStream(stream); - } - } - - public static class TL_upload_saveBigFilePart extends TLObject { - public static int constructor = 0xde7b673d; - - public long file_id; - public int file_part; - public int file_total_parts; - public ByteBufferDesc bytes; - - public Class responseClass () { - return Bool.class; - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(file_id); - stream.writeInt32(file_part); - stream.writeInt32(file_total_parts); - stream.writeByteBuffer(bytes); + stream.writeInt64(req_msg_id); + result.serializeToStream(stream); } @Override @@ -11457,160 +15589,125 @@ public class TLRPC { if (disableFree) { return; } - if (bytes != null) { - BuffersStorage.getInstance().reuseFreeBuffer(bytes); - bytes = null; + if (result != null) { + result.freeResources(); } } } - public static class TL_upload_saveFilePart extends TLObject { - public static int constructor = 0xb304a621; + public static class TL_futureSalt extends TLObject { + public static int constructor = 0x0949d9dc; - public long file_id; - public int file_part; - public ByteBufferDesc bytes; + public int valid_since; + public int valid_until; + public long salt; - public Class responseClass () { - return Bool.class; + public void readParams(AbsSerializedData stream, boolean exception) { + valid_since = stream.readInt32(exception); + valid_until = stream.readInt32(exception); + salt = stream.readInt64(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt64(file_id); - stream.writeInt32(file_part); - stream.writeByteBuffer(bytes); + stream.writeInt32(valid_since); + stream.writeInt32(valid_until); + stream.writeInt64(salt); + } + } + + public static class TL_futuresalts extends TLObject { + public static int constructor = 0xae500895; + + public long req_msg_id; + public int now; + public ArrayList salts = new ArrayList<>(); + + public static TL_futuresalts TLdeserialize(AbsSerializedData stream, int constructor, boolean exception) { + if (TL_futuresalts.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_futuresalts", constructor)); + } else { + return null; + } + } + TL_futuresalts result = new TL_futuresalts(); + result.readParams(stream, exception); + return result; } - @Override - public void freeResources() { - if (disableFree) { - return; + public void readParams(AbsSerializedData stream, boolean exception) { + req_msg_id = stream.readInt64(exception); + now = stream.readInt32(exception); + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_futureSalt salt = new TL_futureSalt(); + salt.readParams(stream, exception); + salts.add(salt); } - if (bytes != null) { - BuffersStorage.getInstance().reuseFreeBuffer(bytes); - bytes = null; + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(req_msg_id); + stream.writeInt32(now); + int count = salts.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + salts.get(a).serializeToStream(stream); } } } - public static class InputEncryptedFile extends TLObject { - public long id; - public long access_hash; - public int parts; - public int key_fingerprint; - public String md5_checksum; - public byte[] key; - public byte[] iv; - } + public static class TL_gzip_packed extends TLObject { + public static int constructor = 0x3072cfa1; - public static class TL_decryptedMessageMediaVideo_old extends TL_decryptedMessageMediaVideo { - public static int constructor = 0x4cee6ef3; + public byte[] packed_data; - - public void readParams(AbsSerializedData stream) { - thumb = stream.readByteArray(); - thumb_w = stream.readInt32(); - thumb_h = stream.readInt32(); - duration = stream.readInt32(); - w = stream.readInt32(); - h = stream.readInt32(); - size = stream.readInt32(); - key = stream.readByteArray(); - iv = stream.readByteArray(); + public void readParams(AbsSerializedData stream, boolean exception) { + packed_data = stream.readByteArray(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeByteArray(thumb); - stream.writeInt32(thumb_w); - stream.writeInt32(thumb_h); - stream.writeInt32(duration); - stream.writeInt32(w); - stream.writeInt32(h); - stream.writeInt32(size); - stream.writeByteArray(key); - stream.writeByteArray(iv); + stream.writeByteArray(packed_data); } } - public static class TL_decryptedMessageMediaAudio_old extends TL_decryptedMessageMediaAudio { - public static int constructor = 0x6080758f; + public static class Vector extends TLObject { + public static int constructor = 0x1cb5c415; + public ArrayList objects = new ArrayList<>(); + } + public static class TL_decryptedMessageHolder extends TLObject { + public static int constructor = 0x555555F9; - public void readParams(AbsSerializedData stream) { - duration = stream.readInt32(); - size = stream.readInt32(); - key = stream.readByteArray(); - iv = stream.readByteArray(); + public long random_id; + public int date; + public TL_decryptedMessageLayer layer; + public EncryptedFile file; + public boolean new_key_used; + + public void readParams(AbsSerializedData stream, boolean exception) { + random_id = stream.readInt64(exception); + date = stream.readInt32(exception); + layer = TL_decryptedMessageLayer.TLdeserialize(stream, stream.readInt32(exception), exception); + if (stream.readBool(exception)) { + file = EncryptedFile.TLdeserialize(stream, stream.readInt32(exception), exception); + } + new_key_used = stream.readBool(exception); } public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(duration); - stream.writeInt32(size); - stream.writeByteArray(key); - stream.writeByteArray(iv); - } - } - - public static class TL_audio_old extends TL_audio { - public static int constructor = 0x427425e7; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - user_id = stream.readInt32(); - date = stream.readInt32(); - duration = stream.readInt32(); - size = stream.readInt32(); - dc_id = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - stream.writeInt32(user_id); + stream.writeInt64(random_id); stream.writeInt32(date); - stream.writeInt32(duration); - stream.writeInt32(size); - stream.writeInt32(dc_id); - } - } - - public static class TL_video_old extends TL_video { - public static int constructor = 0x5a04a49f; - - - public void readParams(AbsSerializedData stream) { - id = stream.readInt64(); - access_hash = stream.readInt64(); - user_id = stream.readInt32(); - date = stream.readInt32(); - caption = stream.readString(); - duration = stream.readInt32(); - size = stream.readInt32(); - thumb = (PhotoSize)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - dc_id = stream.readInt32(); - w = stream.readInt32(); - h = stream.readInt32(); - } - - public void serializeToStream(AbsSerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt64(id); - stream.writeInt64(access_hash); - stream.writeInt32(user_id); - stream.writeInt32(date); - stream.writeString(caption); - stream.writeInt32(duration); - stream.writeInt32(size); - thumb.serializeToStream(stream); - stream.writeInt32(dc_id); - stream.writeInt32(w); - stream.writeInt32(h); + layer.serializeToStream(stream); + stream.writeBool(file != null); + if (file != null) { + file.serializeToStream(stream); + } + stream.writeBool(new_key_used); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/TcpConnection.java b/TMessagesProj/src/main/java/org/telegram/messenger/TcpConnection.java index 3772ed52c..217e63fb1 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/TcpConnection.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/TcpConnection.java @@ -1,9 +1,9 @@ /* - * This is the source code of Telegram for Android v. 1.3.2. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; @@ -20,6 +20,7 @@ import jawnae.pyronet.PyroClient; import jawnae.pyronet.PyroSelector; public class TcpConnection extends ConnectionContext { + public enum TcpConnectionState { TcpConnectionStageIdle, TcpConnectionStageConnecting, @@ -30,8 +31,11 @@ public class TcpConnection extends ConnectionContext { public interface TcpConnectionDelegate { void tcpConnectionClosed(TcpConnection connection); + void tcpConnectionConnected(TcpConnection connection); + void tcpConnectionQuiackAckReceived(TcpConnection connection, int ack); + void tcpConnectionReceivedData(TcpConnection connection, ByteBufferDesc data, int length); } @@ -41,6 +45,7 @@ public class TcpConnection extends ConnectionContext { public volatile int channelToken = 0; private String hostAddress; private int hostPort; + private int currentAddressFlag; private int datacenterId; private int failedConnectionCount; public TcpConnectionDelegate delegate; @@ -62,13 +67,14 @@ public class TcpConnection extends ConnectionContext { if (selector == null) { selector = new PyroSelector(); selector.spawnNetworkThread("network thread"); - BuffersStorage storage = BuffersStorage.getInstance(); + BuffersStorage.getInstance(); } datacenterId = did; connectionState = TcpConnectionState.TcpConnectionStageIdle; } static volatile Integer nextChannelToken = 1; + static int generateChannelToken() { return nextChannelToken++; } @@ -101,8 +107,30 @@ public class TcpConnection extends ConnectionContext { connectionState = TcpConnectionState.TcpConnectionStageConnecting; try { Datacenter datacenter = ConnectionsManager.getInstance().datacenterWithId(datacenterId); - hostAddress = datacenter.getCurrentAddress(); - hostPort = datacenter.getCurrentPort(); + boolean isIpv6 = ConnectionsManager.useIpv6Address(); + if (transportRequestClass == RPCRequest.RPCRequestClassDownloadMedia) { + currentAddressFlag = 2; + hostAddress = datacenter.getCurrentAddress(currentAddressFlag | (isIpv6 ? 1 : 0)); + if (hostAddress == null) { + currentAddressFlag = 0; + hostAddress = datacenter.getCurrentAddress(currentAddressFlag | (isIpv6 ? 1 : 0)); + } + if (hostAddress == null && isIpv6) { + currentAddressFlag = 2; + hostAddress = datacenter.getCurrentAddress(currentAddressFlag); + if (hostAddress == null) { + currentAddressFlag = 0; + hostAddress = datacenter.getCurrentAddress(currentAddressFlag); + } + } + } else { + currentAddressFlag = 0; + hostAddress = datacenter.getCurrentAddress(currentAddressFlag | (isIpv6 ? 1 : 0)); + if (isIpv6 && hostAddress == null) { + hostAddress = datacenter.getCurrentAddress(currentAddressFlag); + } + } + hostPort = datacenter.getCurrentPort(currentAddressFlag); try { synchronized (timerSync) { @@ -163,7 +191,7 @@ public class TcpConnection extends ConnectionContext { } catch (Exception e2) { FileLog.e("tmessages", e2); } - connectionState = TcpConnectionState.TcpConnectionStageReconnecting; + connectionState = TcpConnectionState.TcpConnectionStageReconnecting; if (delegate != null) { final TcpConnectionDelegate finalDelegate = delegate; Utilities.stageQueue.postRunnable(new Runnable() { @@ -186,7 +214,7 @@ public class TcpConnection extends ConnectionContext { isNextPort = true; if (failedConnectionCount > willRetryConnectCount) { Datacenter datacenter = ConnectionsManager.getInstance().datacenterWithId(datacenterId); - datacenter.nextAddressOrPort(); + datacenter.nextAddressOrPort(currentAddressFlag); failedConnectionCount = 0; } } @@ -319,7 +347,7 @@ public class TcpConnection extends ConnectionContext { ByteBufferDesc buffer = BuffersStorage.getInstance().getFreeBuffer(bufferLen); if (firstPacket) { - buffer.writeByte((byte)0xef); + buffer.writeByte((byte) 0xef); firstPacket = false; } if (packetLength < 0x7f) { @@ -354,12 +382,10 @@ public class TcpConnection extends ConnectionContext { ByteBuffer parseLaterBuffer = null; if (restOfTheData != null) { if (lastPacketLength == 0) { - //FileLog.e("tmessages", this + " write addition data to restOfTheData"); if (restOfTheData.capacity() - restOfTheData.position() >= buffer.limit()) { restOfTheData.limit(restOfTheData.position() + buffer.limit()); restOfTheData.put(buffer); buffer = restOfTheData.buffer; - //FileLog.e("tmessages", this + " no need to recreate buffer"); } else { ByteBufferDesc newBuffer = BuffersStorage.getInstance().getFreeBuffer(restOfTheData.limit() + buffer.limit()); restOfTheData.rewind(); @@ -368,30 +394,23 @@ public class TcpConnection extends ConnectionContext { buffer = newBuffer.buffer; BuffersStorage.getInstance().reuseFreeBuffer(restOfTheData); restOfTheData = newBuffer; - //FileLog.e("tmessages", this + " NEED to recreate buffer"); } } else { - //FileLog.e("tmessages", this + " write buffer to restOfTheData buffer of len = " + lastPacketLength); - int len = 0; + int len; if (lastPacketLength - restOfTheData.position() <= buffer.limit()) { len = lastPacketLength - restOfTheData.position(); - //FileLog.e("tmessages", this + " received buffer - OK!"); } else { len = buffer.limit(); - //FileLog.e("tmessages", this + " received buffer less than need"); } int oldLimit = buffer.limit(); buffer.limit(len); restOfTheData.put(buffer); buffer.limit(oldLimit); if (restOfTheData.position() != lastPacketLength) { - //FileLog.e("tmessages", this + " don't get much data to restOfTheData"); return; } else { - //FileLog.e("tmessages", this + " get much data to restOfTheData - OK!"); if (buffer.hasRemaining()) { parseLaterBuffer = buffer; - //FileLog.e("tmessages", this + " something remain in the received buffer"); } else { parseLaterBuffer = null; } @@ -427,10 +446,8 @@ public class TcpConnection extends ConnectionContext { restOfTheData.put(buffer); restOfTheData.limit(restOfTheData.position()); lastPacketLength = 0; - //FileLog.e("tmessages", this + " 1 - size less than 4 bytes - write to free buffer"); if (reuseLater != null) { BuffersStorage.getInstance().reuseFreeBuffer(reuseLater); - //FileLog.e("tmessages", this + " 1 - reuse later buffer1"); } break; } @@ -450,11 +467,10 @@ public class TcpConnection extends ConnectionContext { } if (fByte != 0x7f) { - currentPacketLength = ((int)fByte) * 4; + currentPacketLength = ((int) fByte) * 4; } else { buffer.reset(); if (buffer.remaining() < 4) { - //FileLog.e("tmessages", this + " 2 - size less than 4 bytes - write to free buffer"); if (restOfTheData == null || restOfTheData != null && restOfTheData.position() != 0) { ByteBufferDesc reuseLater = restOfTheData; restOfTheData = BuffersStorage.getInstance().getFreeBuffer(16384); @@ -463,7 +479,6 @@ public class TcpConnection extends ConnectionContext { lastPacketLength = 0; if (reuseLater != null) { BuffersStorage.getInstance().reuseFreeBuffer(reuseLater); - //FileLog.e("tmessages", this + " 2 - reuse later buffer1"); } } else { restOfTheData.position(restOfTheData.limit()); @@ -491,10 +506,8 @@ public class TcpConnection extends ConnectionContext { if (restOfTheData != null && restOfTheData.capacity() < len) { reuseLater = restOfTheData; restOfTheData = null; - //FileLog.e("tmessages", this + " not enough space for len, recreate buffer = " + len); } if (restOfTheData == null) { - //FileLog.e("tmessages", this + " write to restOfTheData, get buffer len = " + len); buffer.reset(); restOfTheData = BuffersStorage.getInstance().getFreeBuffer(len); restOfTheData.put(buffer); @@ -505,7 +518,6 @@ public class TcpConnection extends ConnectionContext { lastPacketLength = len; if (reuseLater != null) { BuffersStorage.getInstance().reuseFreeBuffer(reuseLater); - //FileLog.e("tmessages", this + " 3 - reuse later buffer1"); } return; } @@ -533,17 +545,14 @@ public class TcpConnection extends ConnectionContext { if (lastPacketLength != 0 && restOfTheData.position() == lastPacketLength || lastPacketLength == 0 && !restOfTheData.hasRemaining()) { BuffersStorage.getInstance().reuseFreeBuffer(restOfTheData); restOfTheData = null; - //FileLog.e("tmessages", this + " restOfTheData parsed null it"); } else { restOfTheData.compact(); restOfTheData.limit(restOfTheData.position()); restOfTheData.position(0); - //FileLog.e("tmessages", this + " restOfTheData NOT parsed, compact"); } } if (parseLaterBuffer != null) { - //FileLog.e("tmessages", this + " there is parseLaterBuffer"); buffer = parseLaterBuffer; parseLaterBuffer = null; } @@ -597,7 +606,7 @@ public class TcpConnection extends ConnectionContext { isNextPort = true; if (failedConnectionCount > willRetryConnectCount || switchToNextPort) { Datacenter datacenter = ConnectionsManager.getInstance().datacenterWithId(datacenterId); - datacenter.nextAddressOrPort(); + datacenter.nextAddressOrPort(currentAddressFlag); failedConnectionCount = 0; } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java b/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java index 42e44a7ca..31b0ffce5 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java @@ -1,9 +1,9 @@ /* - * This is the source code of Telegram for Android v. 1.3.2. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; @@ -17,6 +17,7 @@ import org.telegram.android.MessagesStorage; import java.io.File; public class UserConfig { + private static TLRPC.User currentUser; public static boolean registeredForPush = false; public static boolean registeredForInternalPush = false; @@ -31,11 +32,13 @@ public class UserConfig { public static boolean saveIncomingPhotos = false; public static int contactsVersion = 1; public static String passcodeHash = ""; + public static byte[] passcodeSalt = new byte[0]; public static boolean appLocked = false; public static int passcodeType = 0; public static int autoLockIn = 60 * 60; public static int lastPauseTime = 0; public static boolean isWaitingForPasscodeEnter = false; + public static int lastUpdateVersion; public static int getNewMessageId() { int id; @@ -67,10 +70,12 @@ public class UserConfig { editor.putBoolean("registeredForInternalPush", registeredForInternalPush); editor.putBoolean("blockedUsersLoaded", blockedUsersLoaded); editor.putString("passcodeHash1", passcodeHash); + editor.putString("passcodeSalt", passcodeSalt.length > 0 ? Base64.encodeToString(passcodeSalt, Base64.DEFAULT) : ""); editor.putBoolean("appLocked", appLocked); editor.putInt("passcodeType", passcodeType); editor.putInt("autoLockIn", autoLockIn); editor.putInt("lastPauseTime", lastPauseTime); + editor.putInt("lastUpdateVersion", lastUpdateVersion); if (currentUser != null) { if (withFile) { @@ -83,6 +88,7 @@ public class UserConfig { } else { editor.remove("user"); } + editor.commit(); if (oldFile != null) { oldFile.delete(); @@ -123,28 +129,28 @@ public class UserConfig { if (configFile.exists()) { try { SerializedData data = new SerializedData(configFile); - int ver = data.readInt32(); + int ver = data.readInt32(false); if (ver == 1) { - int constructor = data.readInt32(); - currentUser = (TLRPC.TL_userSelf)TLClassStore.Instance().TLdeserialize(data, constructor); - MessagesStorage.lastDateValue = data.readInt32(); - MessagesStorage.lastPtsValue = data.readInt32(); - MessagesStorage.lastSeqValue = data.readInt32(); - registeredForPush = data.readBool(); - pushString = data.readString(); - lastSendMessageId = data.readInt32(); - lastLocalId = data.readInt32(); - contactsHash = data.readString(); - importHash = data.readString(); - saveIncomingPhotos = data.readBool(); + int constructor = data.readInt32(false); + currentUser = TLRPC.TL_userSelf.TLdeserialize(data, constructor, false); + MessagesStorage.lastDateValue = data.readInt32(false); + MessagesStorage.lastPtsValue = data.readInt32(false); + MessagesStorage.lastSeqValue = data.readInt32(false); + registeredForPush = data.readBool(false); + pushString = data.readString(false); + lastSendMessageId = data.readInt32(false); + lastLocalId = data.readInt32(false); + contactsHash = data.readString(false); + importHash = data.readString(false); + saveIncomingPhotos = data.readBool(false); contactsVersion = 0; - MessagesStorage.lastQtsValue = data.readInt32(); - MessagesStorage.lastSecretVersion = data.readInt32(); - int val = data.readInt32(); + MessagesStorage.lastQtsValue = data.readInt32(false); + MessagesStorage.lastSecretVersion = data.readInt32(false); + int val = data.readInt32(false); if (val == 1) { - MessagesStorage.secretPBytes = data.readByteArray(); + MessagesStorage.secretPBytes = data.readByteArray(false); } - MessagesStorage.secretG = data.readInt32(); + MessagesStorage.secretG = data.readInt32(false); Utilities.stageQueue.postRunnable(new Runnable() { @Override public void run() { @@ -152,8 +158,8 @@ public class UserConfig { } }); } else if (ver == 2) { - int constructor = data.readInt32(); - currentUser = (TLRPC.TL_userSelf)TLClassStore.Instance().TLdeserialize(data, constructor); + int constructor = data.readInt32(false); + currentUser = TLRPC.TL_userSelf.TLdeserialize(data, constructor, false); SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("userconfing", Context.MODE_PRIVATE); registeredForPush = preferences.getBoolean("registeredForPush", false); @@ -199,19 +205,61 @@ public class UserConfig { passcodeType = preferences.getInt("passcodeType", 0); autoLockIn = preferences.getInt("autoLockIn", 60 * 60); lastPauseTime = preferences.getInt("lastPauseTime", 0); + lastUpdateVersion = preferences.getInt("lastUpdateVersion", 511); String user = preferences.getString("user", null); if (user != null) { byte[] userBytes = Base64.decode(user, Base64.DEFAULT); if (userBytes != null) { SerializedData data = new SerializedData(userBytes); - currentUser = (TLRPC.TL_userSelf)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + currentUser = TLRPC.TL_userSelf.TLdeserialize(data, data.readInt32(false), false); data.cleanup(); } } + String passcodeSaltString = preferences.getString("passcodeSalt", ""); + if (passcodeSaltString.length() > 0) { + passcodeSalt = Base64.decode(passcodeSaltString, Base64.DEFAULT); + } else { + passcodeSalt = new byte[0]; + } } } } + public static boolean checkPasscode(String passcode) { + if (passcodeSalt.length == 0) { + boolean result = Utilities.MD5(passcode).equals(passcodeHash); + if (result) { + try { + passcodeSalt = new byte[16]; + Utilities.random.nextBytes(passcodeSalt); + byte[] passcodeBytes = passcode.getBytes("UTF-8"); + byte[] bytes = new byte[32 + passcodeBytes.length]; + System.arraycopy(passcodeSalt, 0, bytes, 0, 16); + System.arraycopy(passcodeBytes, 0, bytes, 16, passcodeBytes.length); + System.arraycopy(passcodeSalt, 0, bytes, passcodeBytes.length + 16, 16); + passcodeHash = Utilities.bytesToHex(Utilities.computeSHA256(bytes, 0, bytes.length)); + saveConfig(false); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + return result; + } else { + try { + byte[] passcodeBytes = passcode.getBytes("UTF-8"); + byte[] bytes = new byte[32 + passcodeBytes.length]; + System.arraycopy(passcodeSalt, 0, bytes, 0, 16); + System.arraycopy(passcodeBytes, 0, bytes, 16, passcodeBytes.length); + System.arraycopy(passcodeSalt, 0, bytes, passcodeBytes.length + 16, 16); + String hash = Utilities.bytesToHex(Utilities.computeSHA256(bytes, 0, bytes.length)); + return passcodeHash.equals(hash); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + return false; + } + public static void clearConfig() { currentUser = null; registeredForInternalPush = false; @@ -226,9 +274,11 @@ public class UserConfig { appLocked = false; passcodeType = 0; passcodeHash = ""; + passcodeSalt = new byte[0]; autoLockIn = 60 * 60; lastPauseTime = 0; isWaitingForPasscodeEnter = false; + lastUpdateVersion = BuildVars.BUILD_VERSION; saveConfig(true); } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java b/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java index 3a42f6c6f..73326ac0c 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java @@ -1,43 +1,24 @@ /* - * This is the source code of Telegram for Android v. 1.3.2. + * This is the source code of Telegram for Android v. 2.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * - * Copyright Nikolai Kudashov, 2013. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.messenger; -import android.app.Activity; -import android.content.ContentUris; import android.content.Context; -import android.content.Intent; import android.content.SharedPreferences; -import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.net.Uri; -import android.os.Build; -import android.os.Environment; -import android.provider.DocumentsContract; -import android.provider.MediaStore; -import android.text.SpannableStringBuilder; import android.util.Base64; -import net.hockeyapp.android.CrashManager; -import net.hockeyapp.android.CrashManagerListener; -import net.hockeyapp.android.UpdateManager; - -import org.telegram.android.AndroidUtilities; - import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.math.BigInteger; import java.nio.ByteBuffer; import java.security.KeyFactory; @@ -45,9 +26,7 @@ import java.security.MessageDigest; import java.security.PublicKey; import java.security.SecureRandom; import java.security.spec.RSAPublicKeySpec; -import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Date; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.GZIPInputStream; @@ -56,9 +35,13 @@ import java.util.zip.GZIPOutputStream; import javax.crypto.Cipher; public class Utilities { + public static Pattern pattern = Pattern.compile("[0-9]+"); public static SecureRandom random = new SecureRandom(); + private static byte[] decompressBuffer; + private static ByteArrayOutputStreamExpand decompressStream; + public static ArrayList goodPrimes = new ArrayList<>(); public static class TPFactorizedValue { @@ -68,7 +51,7 @@ public class Utilities { public static volatile DispatchQueue stageQueue = new DispatchQueue("stageQueue"); public static volatile DispatchQueue globalQueue = new DispatchQueue("globalQueue"); public static volatile DispatchQueue searchQueue = new DispatchQueue("searchQueue"); - public static volatile DispatchQueue photoBookQueue = new DispatchQueue("photoBookQueue"); + public static volatile DispatchQueue phoneBookQueue = new DispatchQueue("photoBookQueue"); final protected static char[] hexArray = "0123456789ABCDEF".toCharArray(); @@ -93,9 +76,9 @@ public class Utilities { byte[] bytes = Base64.decode(primes, Base64.DEFAULT); if (bytes != null) { SerializedData data = new SerializedData(bytes); - int count = data.readInt32(); + int count = data.readInt32(false); for (int a = 0; a < count; a++) { - goodPrimes.add(data.readString()); + goodPrimes.add(data.readString(false)); } data.cleanup(); } @@ -108,13 +91,19 @@ public class Utilities { } public native static long doPQNative(long _what); + public native static void loadBitmap(String path, Bitmap bitmap, int scale, int width, int height, int stride); + public native static int pinBitmap(Bitmap bitmap); - public native static void blurBitmap(Object bitmap, int radius); + + public native static void blurBitmap(Object bitmap, int radius, int unpin); + public native static void calcCDT(ByteBuffer hsvBuffer, int width, int height, ByteBuffer buffer); + public native static Bitmap loadWebpImage(ByteBuffer buffer, int len, BitmapFactory.Options options); - public native static Bitmap loadBpgImage(ByteBuffer buffer, int len, BitmapFactory.Options options); + 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) { @@ -284,12 +273,13 @@ public class Utilities { if (arr1 == null || arr2 == null || arr1.length - offset1 != arr2.length - offset2 || arr1.length - offset1 < 0) { return false; } + boolean result = true; for (int a = offset1; a < arr1.length; a++) { if (arr1[a + offset1] != arr2[a + offset2]) { - return false; + result = false; } } - return true; + return result; } public static byte[] computeSHA1(byte[] convertme, int offset, int len) { @@ -318,7 +308,7 @@ public class Utilities { convertme.limit(oldl); convertme.position(oldp); } - return null; + return new byte[0]; } public static byte[] computeSHA1(ByteBuffer convertme) { @@ -359,70 +349,21 @@ public class Utilities { + (((long) bytes[3] & 0xFF) << 24) + (((long) bytes[2] & 0xFF) << 16) + (((long) bytes[1] & 0xFF) << 8) + ((long) bytes[0] & 0xFF); } - public static MessageKeyData generateMessageKeyData(byte[] authKey, byte[] messageKey, boolean incoming) { - MessageKeyData keyData = new MessageKeyData(); - if (authKey == null || authKey.length == 0) { - keyData.aesIv = null; - keyData.aesKey = null; - return keyData; - } - - int x = incoming ? 8 : 0; - - SerializedData data = new SerializedData(); - data.writeRaw(messageKey); - data.writeRaw(authKey, x, 32); - byte[] sha1_a = Utilities.computeSHA1(data.toByteArray()); - data.cleanup(); - - data = new SerializedData(); - data.writeRaw(authKey, 32 + x, 16); - data.writeRaw(messageKey); - data.writeRaw(authKey, 48 + x, 16); - byte[] sha1_b = Utilities.computeSHA1(data.toByteArray()); - data.cleanup(); - - data = new SerializedData(); - data.writeRaw(authKey, 64 + x, 32); - data.writeRaw(messageKey); - byte[] sha1_c = Utilities.computeSHA1(data.toByteArray()); - data.cleanup(); - - data = new SerializedData(); - data.writeRaw(messageKey); - data.writeRaw(authKey, 96 + x, 32); - byte[] sha1_d = Utilities.computeSHA1(data.toByteArray()); - data.cleanup(); - - data = new SerializedData(); - data.writeRaw(sha1_a, 0, 8); - data.writeRaw(sha1_b, 8, 12); - data.writeRaw(sha1_c, 4, 12); - keyData.aesKey = data.toByteArray(); - data.cleanup(); - - data = new SerializedData(); - data.writeRaw(sha1_a, 8, 12); - data.writeRaw(sha1_b, 0, 8); - data.writeRaw(sha1_c, 16, 4); - data.writeRaw(sha1_d, 0, 8); - keyData.aesIv = data.toByteArray(); - data.cleanup(); - - return keyData; - } - - public static TLObject decompress(byte[] data, TLObject parentObject) { - final int BUFFER_SIZE = 512; + public static TLObject decompress(byte[] data, TLObject parentObject, boolean exception) { + final int BUFFER_SIZE = 16384; ByteArrayInputStream is = new ByteArrayInputStream(data); GZIPInputStream gis; + SerializedData stream = null; try { + if (decompressBuffer == null) { + decompressBuffer = new byte[BUFFER_SIZE]; + decompressStream = new ByteArrayOutputStreamExpand(BUFFER_SIZE); + } + decompressStream.reset(); gis = new GZIPInputStream(is, BUFFER_SIZE); - ByteArrayOutputStream bytesOutput = new ByteArrayOutputStream(); - data = new byte[BUFFER_SIZE]; int bytesRead; - while ((bytesRead = gis.read(data)) != -1) { - bytesOutput.write(data, 0, bytesRead); + while ((bytesRead = gis.read(decompressBuffer)) != -1) { + decompressStream.write(decompressBuffer, 0, bytesRead); } try { gis.close(); @@ -434,18 +375,15 @@ public class Utilities { } catch (Exception e) { FileLog.e("tmessages", e); } - SerializedData stream = new SerializedData(bytesOutput.toByteArray()); - try { - bytesOutput.close(); - } catch (Exception e) { - FileLog.e("tmessages", e); - } - TLObject object = TLClassStore.Instance().TLdeserialize(stream, stream.readInt32(), parentObject); - stream.cleanup(); - return object; + stream = new SerializedData(decompressStream.toByteArray()); } catch (IOException e) { FileLog.e("tmessages", e); } + if (stream != null) { + TLObject object = ConnectionsManager.getInstance().deserialize(parentObject, stream, exception); + stream.cleanup(); + return object; + } return null; } @@ -473,42 +411,6 @@ public class Utilities { return packedData; } - public static boolean copyFile(InputStream sourceFile, File destFile) throws IOException { - OutputStream out = new FileOutputStream(destFile); - byte[] buf = new byte[4096]; - int len; - while ((len = sourceFile.read(buf)) > 0) { - Thread.yield(); - out.write(buf, 0, len); - } - out.close(); - return true; - } - - public static boolean copyFile(File sourceFile, File destFile) throws IOException { - if (!destFile.exists()) { - destFile.createNewFile(); - } - FileInputStream source = null; - FileOutputStream destination = null; - try { - source = new FileInputStream(sourceFile); - destination = new FileOutputStream(destFile); - destination.getChannel().transferFrom(source.getChannel(), 0, source.getChannel().size()); - } catch (Exception e) { - FileLog.e("tmessages", e); - return false; - } finally { - if (source != null) { - source.close(); - } - if (destination != null) { - destination.close(); - } - } - return true; - } - public static String MD5(String md5) { if (md5 == null) { return null; @@ -526,249 +428,4 @@ public class Utilities { } return null; } - - public static void addMediaToGallery(String fromPath) { - if (fromPath == null) { - return; - } - File f = new File(fromPath); - Uri contentUri = Uri.fromFile(f); - addMediaToGallery(contentUri); - } - - public static void addMediaToGallery(Uri uri) { - if (uri == null) { - return; - } - Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); - mediaScanIntent.setData(uri); - ApplicationLoader.applicationContext.sendBroadcast(mediaScanIntent); - } - - private static File getAlbumDir() { - File storageDir = null; - if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { - storageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "Telegram"); - if (storageDir != null) { - if (!storageDir.mkdirs()) { - if (!storageDir.exists()){ - FileLog.d("tmessages", "failed to create directory"); - return null; - } - } - } - } else { - FileLog.d("tmessages", "External storage is not mounted READ/WRITE."); - } - - return storageDir; - } - - public static String getPath(final Uri uri) { - try { - final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; - if (isKitKat && DocumentsContract.isDocumentUri(ApplicationLoader.applicationContext, uri)) { - if (isExternalStorageDocument(uri)) { - final String docId = DocumentsContract.getDocumentId(uri); - final String[] split = docId.split(":"); - final String type = split[0]; - if ("primary".equalsIgnoreCase(type)) { - return Environment.getExternalStorageDirectory() + "/" + split[1]; - } - } else if (isDownloadsDocument(uri)) { - final String id = DocumentsContract.getDocumentId(uri); - final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); - return getDataColumn(ApplicationLoader.applicationContext, contentUri, null, null); - } else if (isMediaDocument(uri)) { - final String docId = DocumentsContract.getDocumentId(uri); - final String[] split = docId.split(":"); - final String type = split[0]; - - Uri contentUri = null; - switch (type) { - case "image": - contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; - break; - case "video": - contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; - break; - case "audio": - contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; - break; - } - - final String selection = "_id=?"; - final String[] selectionArgs = new String[] { - split[1] - }; - - return getDataColumn(ApplicationLoader.applicationContext, contentUri, selection, selectionArgs); - } - } else if ("content".equalsIgnoreCase(uri.getScheme())) { - return getDataColumn(ApplicationLoader.applicationContext, uri, null, null); - } else if ("file".equalsIgnoreCase(uri.getScheme())) { - return uri.getPath(); - } - } catch (Exception e) { - FileLog.e("tmessages", e); - } - return null; - } - - public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { - - Cursor cursor = null; - final String column = "_data"; - final String[] projection = { - column - }; - - try { - cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); - if (cursor != null && cursor.moveToFirst()) { - final int column_index = cursor.getColumnIndexOrThrow(column); - return cursor.getString(column_index); - } - } catch (Exception e) { - FileLog.e("tmessages", e); - } finally { - if (cursor != null) { - cursor.close(); - } - } - return null; - } - - public static boolean isExternalStorageDocument(Uri uri) { - return "com.android.externalstorage.documents".equals(uri.getAuthority()); - } - - public static boolean isDownloadsDocument(Uri uri) { - return "com.android.providers.downloads.documents".equals(uri.getAuthority()); - } - - public static boolean isMediaDocument(Uri uri) { - return "com.android.providers.media.documents".equals(uri.getAuthority()); - } - - public static File generatePicturePath() { - try { - File storageDir = getAlbumDir(); - String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); - return new File(storageDir, "IMG_" + timeStamp + ".jpg"); - } catch (Exception e) { - FileLog.e("tmessages", e); - } - return null; - } - - public static CharSequence generateSearchName(String name, String name2, String q) { - if (name == null && name2 == null) { - return ""; - } - SpannableStringBuilder builder = new SpannableStringBuilder(); - String wholeString = name; - if (wholeString == null || wholeString.length() == 0) { - wholeString = name2; - } else if (name2 != null && name2.length() != 0) { - wholeString += " " + name2; - } - wholeString = wholeString.trim(); - String lower = " " + wholeString.toLowerCase(); - - int index = -1; - int lastIndex = 0; - while ((index = lower.indexOf(" " + q, lastIndex)) != -1) { - int idx = index - (index == 0 ? 0 : 1); - int end = q.length() + (index == 0 ? 0 : 1) + idx; - - if (lastIndex != 0 && lastIndex != idx + 1) { - builder.append(wholeString.substring(lastIndex, idx)); - } else if (lastIndex == 0 && idx != 0) { - builder.append(wholeString.substring(0, idx)); - } - - String query = wholeString.substring(idx, end); - if (query.startsWith(" ")) { - builder.append(" "); - } - query.trim(); - builder.append(AndroidUtilities.replaceTags("" + query + "")); - - lastIndex = end; - } - - if (lastIndex != -1 && lastIndex != wholeString.length()) { - builder.append(wholeString.substring(lastIndex, wholeString.length())); - } - - return builder; - } - - public static File generateVideoPath() { - try { - File storageDir = getAlbumDir(); - String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); - return new File(storageDir, "VID_" + timeStamp + ".mp4"); - } catch (Exception e) { - FileLog.e("tmessages", e); - } - return null; - } - - public static String formatFileSize(long size) { - if (size < 1024) { - return String.format("%d B", size); - } else if (size < 1024 * 1024) { - return String.format("%.1f KB", size / 1024.0f); - } else if (size < 1024 * 1024 * 1024) { - return String.format("%.1f MB", size / 1024.0f / 1024.0f); - } else { - return String.format("%.1f GB", size / 1024.0f / 1024.0f / 1024.0f); - } - } - - public static byte[] decodeQuotedPrintable(final byte[] bytes) { - if (bytes == null) { - return null; - } - final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - for (int i = 0; i < bytes.length; i++) { - final int b = bytes[i]; - if (b == '=') { - try { - final int u = Character.digit((char) bytes[++i], 16); - final int l = Character.digit((char) bytes[++i], 16); - buffer.write((char) ((u << 4) + l)); - } catch (Exception e) { - FileLog.e("tmessages", e); - return null; - } - } else { - buffer.write(b); - } - } - byte[] array = buffer.toByteArray(); - try { - buffer.close(); - } catch (Exception e) { - FileLog.e("tmessages", e); - } - return array; - } - - public static void checkForCrashes(Activity context) { - CrashManager.register(context, BuildVars.HOCKEY_APP_HASH, new CrashManagerListener() { - @Override - public boolean includeDeviceData() { - return true; - } - }); - } - - public static void checkForUpdates(Activity context) { - if (BuildVars.DEBUG_VERSION) { - UpdateManager.register(context, BuildVars.HOCKEY_APP_HASH); - } - } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBar.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBar.java index 8c94548bf..7ddad2ce3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBar.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBar.java @@ -24,6 +24,7 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.messenger.R; +import org.telegram.ui.Components.LayoutHelper; public class ActionBar extends FrameLayout { @@ -62,8 +63,8 @@ public class ActionBar extends FrameLayout { titleFrameLayout = new FrameLayout(context); addView(titleFrameLayout); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)titleFrameLayout.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.FILL_PARENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; titleFrameLayout.setLayoutParams(layoutParams); titleFrameLayout.setPadding(0, 0, AndroidUtilities.dp(4), 0); @@ -87,7 +88,7 @@ public class ActionBar extends FrameLayout { } int maxTextWidth = 0; - LayoutParams layoutParams = null; + LayoutParams layoutParams; if (titleTextView != null && titleTextView.getVisibility() == VISIBLE) { if (!AndroidUtilities.isTablet() && getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { @@ -97,8 +98,8 @@ public class ActionBar extends FrameLayout { } layoutParams = (LayoutParams) titleTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; titleTextView.setLayoutParams(layoutParams); titleTextView.measure(width, height); @@ -112,15 +113,15 @@ public class ActionBar extends FrameLayout { } layoutParams = (LayoutParams) subTitleTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; subTitleTextView.setLayoutParams(layoutParams); subTitleTextView.measure(width, height); maxTextWidth = Math.max(maxTextWidth, subTitleTextView.getMeasuredWidth()); } - int x = 0; + int x; if (backButtonImageView != null && backButtonImageView.getVisibility() == VISIBLE) { x = AndroidUtilities.dp(AndroidUtilities.isTablet() ? 80 : 72); } else { @@ -133,7 +134,7 @@ public class ActionBar extends FrameLayout { if (titleTextView != null && titleTextView.getVisibility() == VISIBLE) { layoutParams = (LayoutParams) titleTextView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = titleTextView.getMeasuredHeight(); int y; if (subTitleTextView != null && subTitleTextView.getVisibility() == VISIBLE) { @@ -146,7 +147,7 @@ public class ActionBar extends FrameLayout { } if (subTitleTextView != null && subTitleTextView.getVisibility() == VISIBLE) { layoutParams = (LayoutParams) subTitleTextView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = subTitleTextView.getMeasuredHeight(); layoutParams.setMargins(x, height / 2 + (height / 2 - subTitleTextView.getMeasuredHeight()) / 2 - offset, 0, 0); subTitleTextView.setLayoutParams(layoutParams); @@ -163,7 +164,7 @@ public class ActionBar extends FrameLayout { return; } FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)menu.getLayoutParams(); - layoutParams.width = isSearchFieldVisible ? LayoutParams.MATCH_PARENT : LayoutParams.WRAP_CONTENT; + layoutParams.width = isSearchFieldVisible ? LayoutHelper.MATCH_PARENT : LayoutHelper.WRAP_CONTENT; layoutParams.height = height; layoutParams.leftMargin = isSearchFieldVisible ? AndroidUtilities.dp(AndroidUtilities.isTablet() ? 74 : 66) : 0; layoutParams.topMargin = occupyStatusBar ? AndroidUtilities.statusBarHeight : 0; @@ -277,14 +278,19 @@ public class ActionBar extends FrameLayout { } public void setTitle(CharSequence value) { + boolean created = false; if (value != null && titleTextView == null) { createTitleTextView(); + created = true; } if (titleTextView != null) { lastTitle = value; titleTextView.setVisibility(value != null && !isSearchFieldVisible ? VISIBLE : INVISIBLE); titleTextView.setText(value); positionTitle(getMeasuredWidth(), getMeasuredHeight()); + if (!created) { + titleTextView.setText(value); + } } } @@ -301,11 +307,11 @@ public class ActionBar extends FrameLayout { return subTitleTextView.getCompoundDrawables()[0]; } - public CharSequence getTitle() { + public String getTitle() { if (titleTextView == null) { return null; } - return titleTextView.getText(); + return titleTextView.getText().toString(); } public ActionBarMenu createMenu() { @@ -315,8 +321,8 @@ public class ActionBar extends FrameLayout { menu = new ActionBarMenu(getContext(), this); addView(menu); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)menu.getLayoutParams(); - layoutParams.height = LayoutParams.FILL_PARENT; - layoutParams.width = LayoutParams.WRAP_CONTENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.RIGHT; menu.setLayoutParams(layoutParams); return menu; @@ -331,8 +337,8 @@ public class ActionBar extends FrameLayout { 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.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.topMargin = occupyStatusBar ? AndroidUtilities.statusBarHeight : 0; view.setLayoutParams(layoutParams); } @@ -346,8 +352,8 @@ public class ActionBar extends FrameLayout { 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.height = LayoutHelper.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.RIGHT; actionMode.setLayoutParams(layoutParams); actionMode.setVisibility(INVISIBLE); @@ -358,7 +364,7 @@ public class ActionBar extends FrameLayout { addView(actionModeTop); layoutParams = (FrameLayout.LayoutParams)actionModeTop.getLayoutParams(); layoutParams.height = AndroidUtilities.statusBarHeight; - layoutParams.width = LayoutParams.FILL_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; actionModeTop.setLayoutParams(layoutParams); actionModeTop.setVisibility(INVISIBLE); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java index c49768f8a..b14f21bfe 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java @@ -30,10 +30,11 @@ 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 org.telegram.android.AnimationCompat.AnimatorListenerAdapterProxy; +import org.telegram.android.AnimationCompat.AnimatorSetProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.ViewProxy; +import org.telegram.ui.Components.LayoutHelper; import java.util.ArrayList; @@ -70,7 +71,7 @@ public class ActionBarLayout extends FrameLayout { if (view instanceof ActionBar && view.getVisibility() == VISIBLE) { if (((ActionBar) view).getCastShadows()) { actionBarHeight = view.getMeasuredHeight(); - wasActionBar = true; + //wasActionBar = true; } break; } @@ -108,6 +109,8 @@ public class ActionBarLayout extends FrameLayout { private ActionBar currentActionBar; private AnimatorSetProxy currentAnimation; + private DecelerateInterpolator decelerateInterpolator = new DecelerateInterpolator(1.5f); + private AccelerateDecelerateInterpolator accelerateDecelerateInterpolator = new AccelerateDecelerateInterpolator(); public float innerTranslationX; @@ -128,6 +131,9 @@ public class ActionBarLayout extends FrameLayout { private View backgroundView; private boolean removeActionBarExtraHeight; + private float animationProgress = 0.0f; + private long lastFrameTime; + private String titleOverlayText; private ActionBarLayoutDelegate delegate = null; @@ -151,16 +157,16 @@ public class ActionBarLayout extends FrameLayout { containerViewBack = new LinearLayoutContainer(parentActivity); addView(containerViewBack); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) containerViewBack.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.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.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; containerView.setLayoutParams(layoutParams); @@ -347,8 +353,8 @@ public class ActionBarLayout extends FrameLayout { } containerViewBack.addView(fragmentView); ViewGroup.LayoutParams layoutParams = fragmentView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; fragmentView.setLayoutParams(layoutParams); if (!lastFragment.hasOwnBackground && fragmentView.getBackground() == null) { fragmentView.setBackgroundColor(0xffffffff); @@ -380,7 +386,7 @@ public class ActionBarLayout extends FrameLayout { 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.getPixelsInCM(0.3f, true) && Math.abs(dx) / 3 > dy) { + if (maybeStartTracking && !startedTracking && dx >= AndroidUtilities.getPixelsInCM(0.4f, true) && Math.abs(dx) / 3 > dy) { prepareForMoving(ev); } else if (startedTracking) { if (!beginTrackingSent) { @@ -399,10 +405,10 @@ public class ActionBarLayout extends FrameLayout { velocityTracker = VelocityTracker.obtain(); } velocityTracker.computeCurrentVelocity(1000); - if (!startedTracking) { + if (!startedTracking && fragmentsStack.get(fragmentsStack.size() - 1).swipeBackEnabled) { float velX = velocityTracker.getXVelocity(); float velY = velocityTracker.getYVelocity(); - if (velX >= 3500 && velX > velY) { + if (velX >= 3500 && velX > Math.abs(velY)) { prepareForMoving(ev); if (!beginTrackingSent) { if (((Activity) getContext()).getCurrentFocus() != null) { @@ -418,7 +424,7 @@ public class ActionBarLayout extends FrameLayout { float velX = velocityTracker.getXVelocity(); float velY = velocityTracker.getYVelocity(); final boolean backAnimation = x < containerView.getMeasuredWidth() / 3.0f && (velX < 3500 || velX < velY); - float distToMove = 0; + float distToMove; if (!backAnimation) { distToMove = containerView.getMeasuredWidth() - x; animatorSet.playTogether( @@ -550,6 +556,44 @@ public class ActionBarLayout extends FrameLayout { return presentFragment(fragment, removeLast, false, true); } + private void startLayoutAnimation(final boolean open, final boolean first) { + if (first) { + animationProgress = 0.0f; + lastFrameTime = System.nanoTime() / 1000000; + } + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + if (first) { + transitionAnimationStartTime = System.currentTimeMillis(); + } + long newTime = System.nanoTime() / 1000000; + long dt = newTime - lastFrameTime; + if (dt > 18) { + dt = 18; + } + lastFrameTime = newTime; + animationProgress += dt / 150.0f; + if (animationProgress > 1.0f) { + animationProgress = 1.0f; + } + float interpolated = decelerateInterpolator.getInterpolation(animationProgress); + if (open) { + ViewProxy.setAlpha(containerView, interpolated); + ViewProxy.setTranslationX(containerView, AndroidUtilities.dp(48) * (1.0f - interpolated)); + } else { + ViewProxy.setAlpha(containerViewBack, 1.0f - interpolated); + ViewProxy.setTranslationX(containerViewBack, AndroidUtilities.dp(48) * interpolated); + } + if (animationProgress < 1) { + startLayoutAnimation(open, false); + } else { + onAnimationEndCheck(false); + } + } + }); + } + 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; @@ -586,8 +630,8 @@ public class ActionBarLayout extends FrameLayout { containerViewBack.addView(fragmentView); ViewGroup.LayoutParams layoutParams = fragmentView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; fragmentView.setLayoutParams(layoutParams); fragmentsStack.add(fragment); fragment.onResume(); @@ -615,6 +659,14 @@ public class ActionBarLayout extends FrameLayout { if (useAlphaAnimations && fragmentsStack.size() == 1) { presentFragmentInternalRemoveOld(removeLast, currentFragment); + transitionAnimationStartTime = System.currentTimeMillis(); + transitionAnimationInProgress = true; + onOpenAnimationEndRunnable = new Runnable() { + @Override + public void run() { + fragment.onOpenAnimationEnd(); + } + }; ArrayList animators = new ArrayList<>(); animators.add(ObjectAnimatorProxy.ofFloat(this, "alpha", 0.0f, 1.0f)); if (backgroundView != null) { @@ -622,9 +674,10 @@ public class ActionBarLayout extends FrameLayout { animators.add(ObjectAnimatorProxy.ofFloat(backgroundView, "alpha", 0.0f, 1.0f)); } + fragment.onOpenAnimationStart(); currentAnimation = new AnimatorSetProxy(); currentAnimation.playTogether(animators); - currentAnimation.setInterpolator(new AccelerateDecelerateInterpolator()); + currentAnimation.setInterpolator(accelerateDecelerateInterpolator); currentAnimation.setDuration(200); currentAnimation.addListener(new AnimatorListenerAdapterProxy() { @Override @@ -651,11 +704,13 @@ public class ActionBarLayout extends FrameLayout { }; ViewProxy.setAlpha(containerView, 0.0f); ViewProxy.setTranslationX(containerView, 48.0f); - currentAnimation = new AnimatorSetProxy(); + fragment.onOpenAnimationStart(); + startLayoutAnimation(true, true); + /*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.setInterpolator(decelerateInterpolator); currentAnimation.setDuration(200); currentAnimation.addListener(new AnimatorListenerAdapterProxy() { @Override @@ -673,13 +728,14 @@ public class ActionBarLayout extends FrameLayout { onAnimationEndCheck(false); } }); - currentAnimation.start(); + currentAnimation.start();*/ } } else { if (backgroundView != null) { ViewProxy.setAlpha(backgroundView, 1.0f); backgroundView.setVisibility(VISIBLE); } + fragment.onOpenAnimationStart(); fragment.onOpenAnimationEnd(); } return true; @@ -771,8 +827,8 @@ public class ActionBarLayout extends FrameLayout { } containerView.addView(fragmentView); ViewGroup.LayoutParams layoutParams = fragmentView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; fragmentView.setLayoutParams(layoutParams); previousFragment.onResume(); currentActionBar = previousFragment.actionBar; @@ -794,12 +850,13 @@ public class ActionBarLayout extends FrameLayout { ViewProxy.setTranslationX(containerViewBack, 0); } }; + startLayoutAnimation(false, true); - currentAnimation = new AnimatorSetProxy(); + /*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.setInterpolator(decelerateInterpolator); currentAnimation.setDuration(200); currentAnimation.addListener(new AnimatorListenerAdapterProxy() { @Override @@ -817,7 +874,7 @@ public class ActionBarLayout extends FrameLayout { onAnimationEndCheck(false); } }); - currentAnimation.start(); + currentAnimation.start();*/ } } else { if (useAlphaAnimations) { @@ -846,7 +903,7 @@ public class ActionBarLayout extends FrameLayout { currentAnimation = new AnimatorSetProxy(); currentAnimation.playTogether(animators); - currentAnimation.setInterpolator(new AccelerateDecelerateInterpolator()); + currentAnimation.setInterpolator(accelerateDecelerateInterpolator); currentAnimation.setDuration(200); currentAnimation.addListener(new AnimatorListenerAdapterProxy() { @Override @@ -903,8 +960,8 @@ public class ActionBarLayout extends FrameLayout { } containerView.addView(fragmentView); ViewGroup.LayoutParams layoutParams = fragmentView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; fragmentView.setLayoutParams(layoutParams); previousFragment.onResume(); currentActionBar = previousFragment.actionBar; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenu.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenu.java index 3a63dd188..18ce670aa 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenu.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenu.java @@ -10,13 +10,12 @@ 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; -import android.widget.FrameLayout; import android.widget.LinearLayout; import org.telegram.android.AndroidUtilities; +import org.telegram.ui.Components.LayoutHelper; public class ActionBarMenu extends LinearLayout { @@ -32,21 +31,13 @@ public class ActionBarMenu extends LinearLayout { super(context); } - public ActionBarMenu(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public ActionBarMenu(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - public View addItemResource(int id, int resourceId) { LayoutInflater li = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = li.inflate(resourceId, null); view.setTag(id); addView(view); LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)view.getLayoutParams(); - layoutParams.height = FrameLayout.LayoutParams.FILL_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; view.setBackgroundResource(parentActionBar.itemsBackgroundResourceId); view.setLayoutParams(layoutParams); view.setOnClickListener(new OnClickListener() { @@ -84,7 +75,7 @@ public class ActionBarMenu extends LinearLayout { } addView(menuItem); LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)menuItem.getLayoutParams(); - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.width = width; menuItem.setLayoutParams(layoutParams); menuItem.setOnClickListener(new OnClickListener() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuItem.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuItem.java index e8991c227..6d152b6a2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuItem.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuItem.java @@ -33,18 +33,31 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; import org.telegram.messenger.R; -import org.telegram.ui.AnimationCompat.ViewProxy; +import org.telegram.android.AnimationCompat.ViewProxy; import org.telegram.ui.Components.FrameLayoutFixed; +import org.telegram.ui.Components.LayoutHelper; import java.lang.reflect.Field; public class ActionBarMenuItem extends FrameLayoutFixed { public static class ActionBarMenuItemSearchListener { - public void onSearchExpand() { } - public boolean onSearchCollapse() { return true; } - public void onTextChanged(EditText editText) { } - public void onSearchPressed(EditText editText) { } + public void onSearchExpand() { + } + + public boolean onSearchCollapse() { + return true; + } + + public void onTextChanged(EditText editText) { + } + + public void onSearchPressed(EditText editText) { + } + } + + public interface ActionBarMenuItemDelegate { + void onItemClick(int id); } private ActionBarPopupWindow.ActionBarPopupWindowLayout popupLayout; @@ -62,8 +75,8 @@ public class ActionBarMenuItem extends FrameLayoutFixed { private Runnable showMenuRunnable; private boolean showFromBottom; private int menuHeight = AndroidUtilities.dp(16); - private boolean needOffset = Build.VERSION.SDK_INT >= 21; private int subMenuOpenSide = 0; + private ActionBarMenuItemDelegate delegate; public ActionBarMenuItem(Context context, ActionBarMenu menu, int background) { super(context); @@ -74,8 +87,8 @@ public class ActionBarMenuItem extends FrameLayoutFixed { iconView.setScaleType(ImageView.ScaleType.CENTER); addView(iconView); LayoutParams layoutParams = (LayoutParams) iconView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; iconView.setLayoutParams(layoutParams); } @@ -114,8 +127,8 @@ public class ActionBarMenuItem extends FrameLayoutFixed { for (int a = 0; a < popupLayout.getChildCount(); a++) { View child = popupLayout.getChildAt(a); child.getHitRect(rect); - if ((Integer)child.getTag() < 100) { - if (!rect.contains((int)x, (int)y)) { + if ((Integer) child.getTag() < 100) { + if (!rect.contains((int) x, (int) y)) { child.setPressed(false); child.setSelected(false); if (Build.VERSION.SDK_INT >= 21) { @@ -136,7 +149,11 @@ public class ActionBarMenuItem extends FrameLayoutFixed { } else if (popupWindow != null && popupWindow.isShowing() && event.getActionMasked() == MotionEvent.ACTION_UP) { if (selectedMenuView != null) { selectedMenuView.setSelected(false); - parentMenu.onItemClick((Integer) selectedMenuView.getTag()); + if (parentMenu != null) { + parentMenu.onItemClick((Integer) selectedMenuView.getTag()); + } else if (delegate != null) { + delegate.onItemClick((Integer) selectedMenuView.getTag()); + } } popupWindow.dismiss(); } else { @@ -148,12 +165,12 @@ public class ActionBarMenuItem extends FrameLayoutFixed { return super.onTouchEvent(event); } - public void setShowFromBottom(boolean value) { - showFromBottom = value; + public void setDelegate(ActionBarMenuItemDelegate delegate) { + this.delegate = delegate; } - public void setNeedOffset(boolean value) { - needOffset = Build.VERSION.SDK_INT >= 21 && value; + public void setShowFromBottom(boolean value) { + showFromBottom = value; } public void setSubMenuOpenSide(int side) { @@ -173,7 +190,7 @@ public class ActionBarMenuItem extends FrameLayoutFixed { if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { if (popupWindow != null && popupWindow.isShowing()) { v.getHitRect(rect); - if (!rect.contains((int)event.getX(), (int)event.getY())) { + if (!rect.contains((int) event.getX(), (int) event.getY())) { popupWindow.dismiss(); } } @@ -212,17 +229,21 @@ public class ActionBarMenuItem extends FrameLayoutFixed { } } popupLayout.addView(textView); - LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)textView.getLayoutParams(); + LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) textView.getLayoutParams(); if (LocaleController.isRTL) { layoutParams.gravity = Gravity.RIGHT; } - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(48); textView.setLayoutParams(layoutParams); textView.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { - parentMenu.onItemClick((Integer) view.getTag()); + if (parentMenu != null) { + parentMenu.onItemClick((Integer) view.getTag()); + } else if (delegate != null) { + delegate.onItemClick((Integer) view.getTag()); + } if (popupWindow != null && popupWindow.isShowing()) { popupWindow.dismiss(); } @@ -249,7 +270,7 @@ public class ActionBarMenuItem extends FrameLayoutFixed { return; } if (popupWindow == null) { - popupWindow = new ActionBarPopupWindow(popupLayout, FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT); + popupWindow = new ActionBarPopupWindow(popupLayout, LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT); //popupWindow.setBackgroundDrawable(new BitmapDrawable()); popupWindow.setAnimationStyle(R.style.PopupAnimation); popupWindow.setOutsideTouchable(true); @@ -261,7 +282,7 @@ public class ActionBarMenuItem extends FrameLayoutFixed { popupWindow.getContentView().setOnKeyListener(new OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_MENU && event.getRepeatCount() == 0 && event.getAction() == KeyEvent.ACTION_UP && popupWindow != null && popupWindow.isShowing()) { + if (keyCode == KeyEvent.KEYCODE_MENU && event.getRepeatCount() == 0 && event.getAction() == KeyEvent.ACTION_UP && popupWindow != null && popupWindow.isShowing()) { popupWindow.dismiss(); return true; } @@ -276,8 +297,14 @@ public class ActionBarMenuItem extends FrameLayoutFixed { popupWindow.showAsDropDown(this, -popupLayout.getMeasuredWidth() + getMeasuredWidth(), getOffsetY()); popupWindow.update(this, -popupLayout.getMeasuredWidth() + getMeasuredWidth(), getOffsetY(), -1, -1); } else { - 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); + if (parentMenu != null) { + 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 (getParent() != null) { + View parent = (View) getParent(); + popupWindow.showAsDropDown(this, parent.getMeasuredWidth() - popupLayout.getMeasuredWidth() - getLeft() - parent.getLeft(), getOffsetY()); + popupWindow.update(this, parent.getMeasuredWidth() - popupLayout.getMeasuredWidth() - getLeft() - parent.getLeft(), getOffsetY(), -1, -1); + } } } else { popupWindow.showAsDropDown(this, -AndroidUtilities.dp(8), getOffsetY()); @@ -288,7 +315,12 @@ public class ActionBarMenuItem extends FrameLayoutFixed { if (showFromBottom) { popupWindow.showAsDropDown(this, -popupLayout.getMeasuredWidth() + getMeasuredWidth(), getOffsetY()); } else { - popupWindow.showAsDropDown(this, parentMenu.parentActionBar.getMeasuredWidth() - popupLayout.getMeasuredWidth() - getLeft() - parentMenu.getLeft(), getOffsetY()); + if (parentMenu != null) { + popupWindow.showAsDropDown(this, parentMenu.parentActionBar.getMeasuredWidth() - popupLayout.getMeasuredWidth() - getLeft() - parentMenu.getLeft(), getOffsetY()); + } else { + View parent = (View) getParent(); + popupWindow.showAsDropDown(this, parent.getMeasuredWidth() - popupLayout.getMeasuredWidth() - getLeft() - parent.getLeft(), getOffsetY()); + } } } else { popupWindow.showAsDropDown(this, -AndroidUtilities.dp(8), getOffsetY()); @@ -304,14 +336,14 @@ public class ActionBarMenuItem extends FrameLayoutFixed { if (diff < 0) { y -= diff; } - return y - (needOffset ? AndroidUtilities.statusBarHeight : 0); + return y; } else { - return -getMeasuredHeight() - (needOffset ? AndroidUtilities.statusBarHeight : 0); + return -getMeasuredHeight(); } } public void openSearch() { - if (searchContainer == null || searchContainer.getVisibility() == VISIBLE) { + if (searchContainer == null || searchContainer.getVisibility() == VISIBLE || parentMenu == null) { return; } parentMenu.parentActionBar.onSearchFieldVisibilityChanged(toggleSearch()); @@ -356,13 +388,16 @@ public class ActionBarMenuItem extends FrameLayoutFixed { } public ActionBarMenuItem setIsSearchField(boolean value) { + if (parentMenu == null) { + return this; + } if (value && searchContainer == null) { searchContainer = new FrameLayout(getContext()); parentMenu.addView(searchContainer, 0); - LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)searchContainer.getLayoutParams(); + LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) searchContainer.getLayoutParams(); layoutParams.weight = 1; layoutParams.width = 0; - layoutParams.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.leftMargin = AndroidUtilities.dp(6); searchContainer.setLayoutParams(layoutParams); searchContainer.setVisibility(GONE); @@ -446,7 +481,7 @@ public class ActionBarMenuItem extends FrameLayoutFixed { } searchContainer.addView(searchField); FrameLayout.LayoutParams layoutParams2 = (FrameLayout.LayoutParams) searchField.getLayoutParams(); - layoutParams2.width = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams2.width = LayoutHelper.MATCH_PARENT; layoutParams2.gravity = Gravity.CENTER_VERTICAL; layoutParams2.height = AndroidUtilities.dp(36); layoutParams2.rightMargin = AndroidUtilities.dp(48); @@ -466,7 +501,7 @@ public class ActionBarMenuItem extends FrameLayoutFixed { layoutParams2 = (FrameLayout.LayoutParams) clearButton.getLayoutParams(); layoutParams2.width = AndroidUtilities.dp(48); layoutParams2.gravity = Gravity.CENTER_VERTICAL | Gravity.RIGHT; - layoutParams2.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams2.height = LayoutHelper.MATCH_PARENT; clearButton.setLayoutParams(layoutParams2); } isSearchField = value; @@ -490,7 +525,12 @@ public class ActionBarMenuItem extends FrameLayoutFixed { if (showFromBottom) { popupWindow.update(this, -popupLayout.getMeasuredWidth() + getMeasuredWidth(), getOffsetY(), -1, -1); } else { - popupWindow.update(this, parentMenu.parentActionBar.getMeasuredWidth() - popupLayout.getMeasuredWidth() - getLeft() - parentMenu.getLeft(), getOffsetY(), -1, -1); + if (parentMenu != null) { + popupWindow.update(this, parentMenu.parentActionBar.getMeasuredWidth() - popupLayout.getMeasuredWidth() - getLeft() - parentMenu.getLeft(), getOffsetY(), -1, -1); + } else { + View parent = (View) getParent(); + popupWindow.update(this, parent.getMeasuredWidth() - popupLayout.getMeasuredWidth() - getLeft() - parent.getLeft(), getOffsetY(), -1, -1); + } } } else { popupWindow.update(this, -AndroidUtilities.dp(8), getOffsetY(), -1, -1); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarPopupWindow.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarPopupWindow.java index 479ab5512..759222d1c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarPopupWindow.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarPopupWindow.java @@ -11,12 +11,9 @@ 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; @@ -60,14 +57,6 @@ public class ActionBarPopupWindow extends PopupWindow { super(context); } - public ActionBarPopupWindowLayout(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public ActionBarPopupWindowLayout(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - public void setDispatchKeyEventListener(OnDispatchKeyEventListener listener) { mOnDispatchKeyEventListener = listener; } @@ -91,21 +80,6 @@ public class ActionBarPopupWindow extends PopupWindow { init(); } - public ActionBarPopupWindow(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - public ActionBarPopupWindow(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - init(); - } - - public ActionBarPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - init(); - } - public ActionBarPopupWindow(int width, int height) { super(width, height); init(); @@ -135,15 +109,15 @@ public class ActionBarPopupWindow extends PopupWindow { mSuperScrollListener = null; } } - if (Build.VERSION.SDK_INT >= 21) { + /*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 */ + //ignored } - } + }*/ } private void unregisterListener() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BaseFragment.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BaseFragment.java index b6592a2d9..c4414cc87 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BaseFragment.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BaseFragment.java @@ -9,7 +9,7 @@ package org.telegram.ui.ActionBar; import android.app.Activity; -import android.app.AlertDialog; +import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -23,8 +23,9 @@ import org.telegram.messenger.FileLog; import org.telegram.messenger.R; public class BaseFragment { + private boolean isFinished = false; - protected AlertDialog visibleDialog = null; + protected Dialog visibleDialog = null; protected View fragmentView; protected ActionBarLayout parentLayout; @@ -191,7 +192,11 @@ public class BaseFragment { } } - public void onOpenAnimationEnd() { + protected void onOpenAnimationEnd() { + + } + + protected void onOpenAnimationStart() { } @@ -203,8 +208,8 @@ public class BaseFragment { return true; } - public AlertDialog showAlertDialog(AlertDialog.Builder builder) { - if (parentLayout == null || parentLayout.checkTransitionAnimation() || parentLayout.animationInProgress || parentLayout.startedTracking) { + public Dialog showDialog(Dialog dialog) { + if (parentLayout == null || parentLayout.animationInProgress || parentLayout.startedTracking || parentLayout.checkTransitionAnimation()) { return null; } try { @@ -216,7 +221,7 @@ public class BaseFragment { FileLog.e("tmessages", e); } try { - visibleDialog = builder.show(); + visibleDialog = dialog; visibleDialog.setCanceledOnTouchOutside(true); visibleDialog.setOnDismissListener(new DialogInterface.OnDismissListener() { @Override @@ -225,6 +230,7 @@ public class BaseFragment { onDialogDismiss(); } }); + visibleDialog.show(); return visibleDialog; } catch (Exception e) { FileLog.e("tmessages", e); @@ -235,4 +241,8 @@ public class BaseFragment { protected void onDialogDismiss() { } + + public void setVisibleDialog(Dialog dialog) { + visibleDialog = dialog; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheet.java new file mode 100644 index 000000000..39ca1cf64 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheet.java @@ -0,0 +1,301 @@ +/* + * This is the source code of Telegram for Android v. 2.x.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2015. + */ + +package org.telegram.ui.ActionBar; + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Build; +import android.os.Bundle; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.TextView; + +import org.telegram.android.AndroidUtilities; +import org.telegram.android.AnimationCompat.AnimatorListenerAdapterProxy; +import org.telegram.android.AnimationCompat.AnimatorSetProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.LocaleController; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.R; +import org.telegram.ui.Components.LayoutHelper; + +public class BottomSheet extends Dialog { + + private LinearLayout linearLayout; + private FrameLayout container; + private boolean dismissed; + + private DialogInterface.OnClickListener onClickListener; + + private CharSequence[] items; + private View customView; + private boolean overrideTabletWidth = true; + + private BottomSheetDelegate delegate; + + public interface BottomSheetDelegate { + void onOpenAnimationEnd(); + } + + private static class BottomSheetRow extends FrameLayout { + + private TextView textView; + + public BottomSheetRow(Context context) { + super(context); + + setBackgroundResource(R.drawable.list_selector); + setPadding(AndroidUtilities.dp(16), 0, AndroidUtilities.dp(16), 0); + + textView = new TextView(context); + textView.setTextColor(0xff212121); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL)); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(48), MeasureSpec.EXACTLY)); + } + } + + public BottomSheet(Context context) { + super(context); + + container = new FrameLayout(getContext()); + container.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + dismiss(); + return false; + } + }); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + /* + @null + @null + fill_parent + @android:color/transparent + true + @null + true + @style/BottomSheet.Animation + #DD000000 + #8A000000 + #42000000 + */ + + Window window = getWindow(); + window.setBackgroundDrawableResource(R.drawable.transparent); + window.requestFeature(Window.FEATURE_NO_TITLE); + + setContentView(container); + + linearLayout = new LinearLayout(getContext()); + linearLayout.setOrientation(LinearLayout.VERTICAL); + if (AndroidUtilities.isTablet() && !overrideTabletWidth) { + container.addView(linearLayout, 0, LayoutHelper.createFrame(320, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL)); + } else { + container.addView(linearLayout, 0, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM)); + } + + View shadow = new View(getContext()); + shadow.setBackgroundResource(R.drawable.header_shadow_reverse); + linearLayout.addView(shadow, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 3)); + + LinearLayout containerView = new LinearLayout(getContext()); + containerView.setBackgroundColor(0xffffffff); + containerView.setOrientation(LinearLayout.VERTICAL); + containerView.setPadding(0, AndroidUtilities.dp(8), 0, AndroidUtilities.dp(8)); + linearLayout.addView(containerView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + if (items != null) { + for (int a = 0; a < items.length; a++) { + CharSequence charSequence = items[a]; + BottomSheetRow row = new BottomSheetRow(getContext()); + row.textView.setText(charSequence); + containerView.addView(row, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); + row.setTag(a); + row.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + dismissWithButtonClick((Integer) v.getTag()); + } + }); + } + } + if (customView != null) { + containerView.addView(customView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + } + + WindowManager.LayoutParams params = getWindow().getAttributes(); + params.height = ViewGroup.LayoutParams.MATCH_PARENT; + params.width = ViewGroup.LayoutParams.MATCH_PARENT; + params.gravity = Gravity.TOP | Gravity.LEFT; + params.dimAmount = 0.2f; + params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; + if (Build.VERSION.SDK_INT >= 21) { + params.flags |= WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; + params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; + } + getWindow().setAttributes(params); + + setOnShowListener(new OnShowListener() { + @Override + public void onShow(DialogInterface dialog) { + AnimatorSetProxy animatorSetProxy = new AnimatorSetProxy(); + animatorSetProxy.playTogether( + ObjectAnimatorProxy.ofFloat(linearLayout, "translationY", linearLayout.getHeight(), 0)); + animatorSetProxy.setDuration(180); + animatorSetProxy.setInterpolator(new DecelerateInterpolator()); + animatorSetProxy.addListener(new AnimatorListenerAdapterProxy() { + @Override + public void onAnimationEnd(Object animation) { + if (delegate != null) { + delegate.onOpenAnimationEnd(); + } + } + }); + animatorSetProxy.start(); + } + }); + } + + public void setDelegate(BottomSheetDelegate delegate) { + this.delegate = delegate; + } + + public FrameLayout getContainer() { + return container; + } + + public LinearLayout getSheetContainer() { + return linearLayout; + } + + private void dismissWithButtonClick(final int item) { + if (dismissed) { + return; + } + AnimatorSetProxy animatorSetProxy = new AnimatorSetProxy(); + animatorSetProxy.playTogether( + ObjectAnimatorProxy.ofFloat(linearLayout, "translationY", linearLayout.getHeight() + AndroidUtilities.dp(10)) + ); + animatorSetProxy.setDuration(180); + animatorSetProxy.setInterpolator(new AccelerateInterpolator()); + animatorSetProxy.addListener(new AnimatorListenerAdapterProxy() { + @Override + public void onAnimationEnd(Object animation) { + if (onClickListener != null) { + onClickListener.onClick(BottomSheet.this, item); + } + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + try { + BottomSheet.super.dismiss(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + }); + } + + @Override + public void onAnimationCancel(Object animation) { + onAnimationEnd(animation); + } + }); + animatorSetProxy.start(); + } + + @Override + public void dismiss() { + if (dismissed) { + return; + } + dismissed = true; + AnimatorSetProxy animatorSetProxy = new AnimatorSetProxy(); + animatorSetProxy.playTogether( + ObjectAnimatorProxy.ofFloat(linearLayout, "translationY", linearLayout.getHeight() + AndroidUtilities.dp(10)) + ); + animatorSetProxy.setDuration(180); + animatorSetProxy.setInterpolator(new AccelerateInterpolator()); + animatorSetProxy.addListener(new AnimatorListenerAdapterProxy() { + @Override + public void onAnimationEnd(Object animation) { + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + try { + BottomSheet.super.dismiss(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + }); + } + + @Override + public void onAnimationCancel(Object animation) { + onAnimationEnd(animation); + } + }); + animatorSetProxy.start(); + } + + public static class Builder { + + private BottomSheet bottomSheet; + + public Builder(Context context) { + bottomSheet = new BottomSheet(context); + } + + public Builder setItems(CharSequence[] items, final OnClickListener onClickListener) { + bottomSheet.items = items; + bottomSheet.onClickListener = onClickListener; + return this; + } + + public Builder setCustomView(View view) { + bottomSheet.customView = view; + return this; + } + + public BottomSheet create() { + return bottomSheet; + } + + public BottomSheet show() { + bottomSheet.show(); + return bottomSheet; + } + + public BottomSheet setOverrideTabletWidth(boolean value) { + bottomSheet.overrideTabletWidth = value; + return bottomSheet; + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/DrawerLayoutContainer.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/DrawerLayoutContainer.java index aa136bf16..3596048d4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/DrawerLayoutContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/DrawerLayoutContainer.java @@ -26,9 +26,9 @@ 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; +import org.telegram.android.AnimationCompat.AnimatorListenerAdapterProxy; +import org.telegram.android.AnimationCompat.AnimatorSetProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; public class DrawerLayoutContainer extends FrameLayout { @@ -57,6 +57,7 @@ public class DrawerLayoutContainer extends FrameLayout { private float drawerPosition = 0; private boolean drawerOpened = false; + private boolean allowDrawContent = true; public DrawerLayoutContainer(Context context) { super(context); @@ -150,7 +151,7 @@ public class DrawerLayoutContainer extends FrameLayout { if (drawerLayout.getVisibility() != newVisibility) { drawerLayout.setVisibility(newVisibility); } - setScrimOpacity(drawerPosition / (float)drawerLayout.getMeasuredWidth()); + setScrimOpacity(drawerPosition / (float) drawerLayout.getMeasuredWidth()); } public float getDrawerPosition() { @@ -276,6 +277,13 @@ public class DrawerLayoutContainer extends FrameLayout { return drawerOpened; } + public void setAllowDrawContent(boolean value) { + if (allowDrawContent != value) { + allowDrawContent = value; + invalidate(); + } + } + public boolean onTouchEvent(MotionEvent ev) { if (!parentActionBarLayout.checkTransitionAnimation()) { if (drawerOpened && ev != null && ev.getX() > drawerPosition && !startedTracking) { @@ -301,7 +309,7 @@ public class DrawerLayoutContainer extends FrameLayout { 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) && Math.abs(dx) >= AndroidUtilities.getPixelsInCM(0.2f, true) || dx < 0 && Math.abs(dx) >= Math.abs(dy) && Math.abs(dx) >= AndroidUtilities.getPixelsInCM(0.3f, true))) { + if (maybeStartTracking && !startedTracking && (dx > 0 && dx / 3.0f > Math.abs(dy) && Math.abs(dx) >= AndroidUtilities.getPixelsInCM(0.2f, true) || dx < 0 && Math.abs(dx) >= Math.abs(dy) && Math.abs(dx) >= AndroidUtilities.getPixelsInCM(0.4f, true))) { prepareForDrawerOpen(ev); startedTrackingX = (int) ev.getX(); requestDisallowInterceptTouchEvent(true); @@ -445,6 +453,9 @@ public class DrawerLayoutContainer extends FrameLayout { @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + if (!allowDrawContent) { + return false; + } final int height = getHeight(); final boolean drawingContent = child != drawerLayout; int clipLeft = 0, clipRight = getWidth(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/BaseLocationAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/BaseLocationAdapter.java new file mode 100644 index 000000000..81d26e865 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/BaseLocationAdapter.java @@ -0,0 +1,222 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Adapters; + +import android.location.Location; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.telegram.android.AndroidUtilities; +import org.telegram.android.volley.Request; +import org.telegram.android.volley.RequestQueue; +import org.telegram.android.volley.Response; +import org.telegram.android.volley.VolleyError; +import org.telegram.android.volley.toolbox.JsonObjectRequest; +import org.telegram.android.volley.toolbox.Volley; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.BuildVars; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.TLRPC; + +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Locale; +import java.util.Timer; +import java.util.TimerTask; + +public class BaseLocationAdapter extends BaseFragmentAdapter { + + public interface BaseLocationAdapterDelegate { + void didLoadedSearchResult(ArrayList places); + } + + private RequestQueue requestQueue; + protected boolean searching; + protected ArrayList places = new ArrayList<>(); + protected ArrayList iconUrls = new ArrayList<>(); + private Location lastSearchLocation; + private BaseLocationAdapterDelegate delegate; + private Timer searchTimer; + + public BaseLocationAdapter() { + requestQueue = Volley.newRequestQueue(ApplicationLoader.applicationContext); + } + + public void destroy() { + if (requestQueue != null) { + requestQueue.cancelAll("search"); + requestQueue.stop(); + } + } + + public void setDelegate(BaseLocationAdapterDelegate delegate) { + this.delegate = delegate; + } + + public void searchDelayed(final String query, final Location coordinate) { + if (query == null || query.length() == 0) { + places.clear(); + notifyDataSetChanged(); + } 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); + } + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + lastSearchLocation = null; + searchGooglePlacesWithQuery(query, coordinate); + } + }); + } + }, 200, 500); + } + } + + public void searchGooglePlacesWithQuery(final String query, final Location coordinate) { + if (lastSearchLocation != null && coordinate.distanceTo(lastSearchLocation) < 200) { + return; + } + lastSearchLocation = coordinate; + if (searching) { + searching = false; + requestQueue.cancelAll("search"); + } + try { + searching = true; + String url = String.format(Locale.US, "https://api.foursquare.com/v2/venues/search/?v=%s&locale=en&limit=25&client_id=%s&client_secret=%s&ll=%s", BuildVars.FOURSQUARE_API_VERSION, BuildVars.FOURSQUARE_API_ID, BuildVars.FOURSQUARE_API_KEY, String.format(Locale.US, "%f,%f", coordinate.getLatitude(), coordinate.getLongitude())); + if (query != null && query.length() > 0) { + url += "&query=" + URLEncoder.encode(query, "UTF-8"); + } + JsonObjectRequest jsonObjReq = new JsonObjectRequest(Request.Method.GET, url, null, + new Response.Listener() { + @Override + public void onResponse(JSONObject response) { + try { + places.clear(); + iconUrls.clear(); + /* + GOOGLE MAPS + JSONArray result = response.getJSONArray("results"); + + for (int a = 0; a < result.length(); a++) { + try { + JSONObject object = result.getJSONObject(a); + JSONObject location = object.getJSONObject("geometry").getJSONObject("location"); + TLRPC.TL_messageMediaVenue venue = new TLRPC.TL_messageMediaVenue(); + venue.geo = new TLRPC.TL_geoPoint(); + venue.geo.lat = location.getDouble("lat"); + venue.geo._long = location.getDouble("lng"); + if (object.has("vicinity")) { + venue.address = object.getString("vicinity").trim(); + } else { + venue.address = String.format(Locale.US, "%f,%f", venue.geo.lat, venue.geo._long); + } + if (object.has("name")) { + venue.title = object.getString("name").trim(); + } + venue.venue_id = object.getString("place_id"); + venue.provider = "google"; + places.add(venue); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + */ + JSONArray result = response.getJSONObject("response").getJSONArray("venues"); + + for (int a = 0; a < result.length(); a++) { + try { + JSONObject object = result.getJSONObject(a); + String iconUrl = null; + if (object.has("categories")) { + JSONArray categories = object.getJSONArray("categories"); + if (categories.length() > 0) { + JSONObject category = categories.getJSONObject(0); + if (category.has("icon")) { + JSONObject icon = category.getJSONObject("icon"); + iconUrl = String.format(Locale.US, "%s64%s", icon.getString("prefix"), icon.getString("suffix")); + } + } + } + iconUrls.add(iconUrl); + + JSONObject location = object.getJSONObject("location"); + TLRPC.TL_messageMediaVenue venue = new TLRPC.TL_messageMediaVenue(); + venue.geo = new TLRPC.TL_geoPoint(); + venue.geo.lat = location.getDouble("lat"); + venue.geo._long = location.getDouble("lng"); + if (location.has("address")) { + venue.address = location.getString("address"); + } else if (location.has("city")) { + venue.address = location.getString("city"); + } else if (location.has("state")) { + venue.address = location.getString("state"); + } else if (location.has("country")) { + venue.address = location.getString("country"); + } else { + venue.address = String.format(Locale.US, "%f,%f", venue.geo.lat, venue.geo._long); + } + if (object.has("name")) { + venue.title = object.getString("name"); + } + venue.venue_id = object.getString("id"); + venue.provider = "foursquare"; + places.add(venue); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + searching = false; + notifyDataSetChanged(); + if (delegate != null) { + delegate.didLoadedSearchResult(places); + } + } + }, + new Response.ErrorListener() { + @Override + public void onErrorResponse(VolleyError error) { + FileLog.e("tmessages", "Error: " + error.getMessage()); + searching = false; + notifyDataSetChanged(); + if (delegate != null) { + delegate.didLoadedSearchResult(places); + } + } + }); + jsonObjReq.setTag("search"); + requestQueue.add(jsonObjReq); + } catch (Exception e) { + FileLog.e("tmessages", e); + searching = false; + if (delegate != null) { + delegate.didLoadedSearchResult(places); + } + } + notifyDataSetChanged(); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/BaseSearchAdapterRecycler.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/BaseSearchAdapterRecycler.java new file mode 100644 index 000000000..4f24e7635 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/BaseSearchAdapterRecycler.java @@ -0,0 +1,210 @@ +/* + * This is the source code of Telegram for Android v. 2.x.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2015. + */ + +package org.telegram.ui.Adapters; + +import org.telegram.SQLite.SQLiteCursor; +import org.telegram.SQLite.SQLitePreparedStatement; +import org.telegram.android.AndroidUtilities; +import org.telegram.android.MessagesStorage; +import org.telegram.android.support.widget.RecyclerView; +import org.telegram.messenger.ConnectionsManager; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.RPCRequest; +import org.telegram.messenger.TLObject; +import org.telegram.messenger.TLRPC; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public abstract class BaseSearchAdapterRecycler extends RecyclerView.Adapter { + + protected static class HashtagObject { + String hashtag; + int date; + } + + protected ArrayList globalSearch = new ArrayList<>(); + private long reqId = 0; + private int lastReqId; + protected String lastFoundUsername = null; + + protected ArrayList hashtags; + protected HashMap hashtagsByText; + protected boolean hashtagsLoadedFromDb = false; + + public void queryServerSearch(final String query) { + if (reqId != 0) { + ConnectionsManager.getInstance().cancelRpc(reqId, true); + reqId = 0; + } + if (query == null || query.length() < 5) { + globalSearch.clear(); + lastReqId = 0; + notifyDataSetChanged(); + return; + } + TLRPC.TL_contacts_search req = new TLRPC.TL_contacts_search(); + req.q = query; + req.limit = 50; + final int currentReqId = ++lastReqId; + 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.TL_contacts_found res = (TLRPC.TL_contacts_found) response; + globalSearch = res.users; + lastFoundUsername = query; + notifyDataSetChanged(); + } + } + reqId = 0; + } + }); + } + }, true, RPCRequest.RPCRequestClassGeneric | RPCRequest.RPCRequestClassFailOnServerErrors); + } + + public void loadRecentHashtags() { + MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() { + @Override + public void run() { + try { + SQLiteCursor cursor = MessagesStorage.getInstance().getDatabase().queryFinalized("SELECT id, date FROM hashtag_recent_v2 WHERE 1"); + final ArrayList arrayList = new ArrayList<>(); + final HashMap hashMap = new HashMap<>(); + while (cursor.next()) { + HashtagObject hashtagObject = new HashtagObject(); + hashtagObject.hashtag = cursor.stringValue(0); + hashtagObject.date = cursor.intValue(1); + arrayList.add(hashtagObject); + hashMap.put(hashtagObject.hashtag, hashtagObject); + } + cursor.dispose(); + Collections.sort(arrayList, new Comparator() { + @Override + public int compare(HashtagObject lhs, HashtagObject rhs) { + if (lhs.date < rhs.date) { + return 1; + } else if (lhs.date > rhs.date) { + return -1; + } else { + return 0; + } + } + }); + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + setHashtags(arrayList, hashMap); + } + }); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + }); + } + + public void addHashtagsFromMessage(String message) { + if (message == null) { + return; + } + boolean changed = false; + Pattern pattern = Pattern.compile("(^|\\s)#[\\w@\\.]+"); + Matcher matcher = pattern.matcher(message); + while (matcher.find()) { + int start = matcher.start(); + int end = matcher.end(); + if (message.charAt(start) != '@' && message.charAt(start) != '#') { + start++; + } + String hashtag = message.substring(start, end); + if (hashtagsByText == null) { + hashtagsByText = new HashMap<>(); + hashtags = new ArrayList<>(); + } + HashtagObject hashtagObject = hashtagsByText.get(hashtag); + if (hashtagObject == null) { + hashtagObject = new HashtagObject(); + hashtagObject.hashtag = hashtag; + hashtagsByText.put(hashtagObject.hashtag, hashtagObject); + } else { + hashtags.remove(hashtagObject); + } + hashtagObject.date = (int) (System.currentTimeMillis() / 1000); + hashtags.add(0, hashtagObject); + changed = true; + } + if (changed) { + putRecentHashtags(hashtags); + } + } + + private void putRecentHashtags(final ArrayList arrayList) { + MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() { + @Override + public void run() { + try { + MessagesStorage.getInstance().getDatabase().beginTransaction(); + SQLitePreparedStatement state = MessagesStorage.getInstance().getDatabase().executeFast("REPLACE INTO hashtag_recent_v2 VALUES(?, ?)"); + for (int a = 0; a < arrayList.size(); a++) { + if (a == 100) { + break; + } + HashtagObject hashtagObject = arrayList.get(a); + state.requery(); + state.bindString(1, hashtagObject.hashtag); + state.bindInteger(2, hashtagObject.date); + state.step(); + } + state.dispose(); + MessagesStorage.getInstance().getDatabase().commitTransaction(); + if (arrayList.size() >= 100) { + MessagesStorage.getInstance().getDatabase().beginTransaction(); + for (int a = 100; a < arrayList.size(); a++) { + MessagesStorage.getInstance().getDatabase().executeFast("DELETE FROM hashtag_recent_v2 WHERE id = '" + arrayList.get(a).hashtag + "'").stepThis().dispose(); + } + MessagesStorage.getInstance().getDatabase().commitTransaction(); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + }); + } + + public void clearRecentHashtags() { + hashtags = new ArrayList<>(); + hashtagsByText = new HashMap<>(); + MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() { + @Override + public void run() { + try { + MessagesStorage.getInstance().getDatabase().executeFast("DELETE FROM hashtag_recent_v2 WHERE 1").stepThis().dispose(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + }); + } + + protected void setHashtags(ArrayList arrayList, HashMap hashMap) { + hashtags = arrayList; + hashtagsByText = hashMap; + hashtagsLoadedFromDb = true; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ChatActivityAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ChatActivityAdapter.java new file mode 100644 index 000000000..afe18248d --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ChatActivityAdapter.java @@ -0,0 +1,396 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Adapters; + +public class ChatActivityAdapter { + + /*private Context mContext; + + public ChatAdapter(Context context) { + mContext = context; + } + + @Override + public boolean areAllItemsEnabled() { + return true; + } + + @Override + public boolean isEnabled(int i) { + return true; + } + + @Override + public int getCount() { + int count = messages.size(); + if (count != 0) { + if (!endReached) { + count++; + } + if (!forward_end_reached) { + count++; + } + } + return count; + } + + @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 offset = 1; + if ((!endReached || !forward_end_reached) && messages.size() != 0) { + if (!endReached) { + offset = 0; + } + if (i == 0 && !endReached || !forward_end_reached && i == (messages.size() + 1 - offset)) { + View progressBar = null; + if (view == null) { + LayoutInflater li = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = li.inflate(R.layout.chat_loading_layout, viewGroup, false); + progressBar = view.findViewById(R.id.progressLayout); + if (ApplicationLoader.isCustomTheme()) { + progressBar.setBackgroundResource(R.drawable.system_loader2); + } else { + progressBar.setBackgroundResource(R.drawable.system_loader1); + } + } else { + progressBar = view.findViewById(R.id.progressLayout); + } + progressBar.setVisibility(loadsCount > 1 ? View.VISIBLE : View.INVISIBLE); + + return view; + } + } + final MessageObject message = messages.get(messages.size() - i - offset); + int type = message.contentType; + if (view == null) { + if (type == 0) { + view = new ChatMessageCell(mContext); + } + if (type == 1) { + view = new ChatMediaCell(mContext); + } else if (type == 2) { + view = new ChatAudioCell(mContext); + } else if (type == 3) { + view = new ChatContactCell(mContext); + } else if (type == 6) { + LayoutInflater li = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = li.inflate(R.layout.chat_unread_layout, viewGroup, false); + } else if (type == 4) { + view = new ChatActionCell(mContext); + } + + if (view instanceof ChatBaseCell) { + ((ChatBaseCell) view).setDelegate(new ChatBaseCell.ChatBaseCellDelegate() { + @Override + public void didPressedUserAvatar(ChatBaseCell cell, TLRPC.User user) { + if (actionBar.isActionModeShowed()) { + processRowSelect(cell); + return; + } + if (user != null && user.id != UserConfig.getClientUserId()) { + Bundle args = new Bundle(); + args.putInt("user_id", user.id); + presentFragment(new ProfileActivity(args)); + } + } + + @Override + public void didPressedCancelSendButton(ChatBaseCell cell) { + MessageObject message = cell.getMessageObject(); + if (message.messageOwner.send_state != 0) { + SendMessagesHelper.getInstance().cancelSendingMessage(message); + } + } + + @Override + public void didLongPressed(ChatBaseCell cell) { + createMenu(cell, false); + } + + @Override + public boolean canPerformActions() { + return actionBar != null && !actionBar.isActionModeShowed(); + } + + @Override + public void didPressUrl(String url) { + if (url.startsWith("@")) { + openProfileWithUsername(url.substring(1)); + } else if (url.startsWith("#")) { + MessagesActivity fragment = new MessagesActivity(null); + fragment.setSearchString(url); + presentFragment(fragment); + } + } + + @Override + public void didPressReplyMessage(ChatBaseCell cell, int id) { + scrollToMessageId(id, cell.getMessageObject().getId(), true); + } + }); + if (view instanceof ChatMediaCell) { + ((ChatMediaCell) view).setAllowedToSetPhoto(openAnimationEnded); + ((ChatMediaCell) view).setMediaDelegate(new ChatMediaCell.ChatMediaCellDelegate() { + @Override + public void didClickedImage(ChatMediaCell cell) { + MessageObject message = cell.getMessageObject(); + if (message.isSendError()) { + createMenu(cell, false); + return; + } else if (message.isSending()) { + return; + } + if (message.type == 1) { + PhotoViewer.getInstance().setParentActivity(getParentActivity()); + PhotoViewer.getInstance().openPhoto(message, ChatActivity.this); + } else if (message.type == 3) { + sendSecretMessageRead(message); + try { + File f = null; + if (message.messageOwner.attachPath != null && message.messageOwner.attachPath.length() != 0) { + f = new File(message.messageOwner.attachPath); + } + if (f == null || f != null && !f.exists()) { + f = FileLoader.getPathToMessage(message.messageOwner); + } + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setDataAndType(Uri.fromFile(f), "video/mp4"); + getParentActivity().startActivityForResult(intent, 500); + } catch (Exception e) { + alertUserOpenError(message); + } + } else if (message.type == 4) { + if (!isGoogleMapsInstalled()) { + return; + } + LocationActivity fragment = new LocationActivity(); + fragment.setMessageObject(message); + presentFragment(fragment); + } else if (message.type == 9) { + File f = null; + String fileName = message.getFileName(); + if (message.messageOwner.attachPath != null && message.messageOwner.attachPath.length() != 0) { + f = new File(message.messageOwner.attachPath); + } + if (f == null || f != null && !f.exists()) { + f = FileLoader.getPathToMessage(message.messageOwner); + } + if (f != null && f.exists()) { + String realMimeType = null; + try { + Intent intent = new Intent(Intent.ACTION_VIEW); + if (message.type == 8 || message.type == 9) { + MimeTypeMap myMime = MimeTypeMap.getSingleton(); + int idx = fileName.lastIndexOf("."); + if (idx != -1) { + String ext = fileName.substring(idx + 1); + realMimeType = myMime.getMimeTypeFromExtension(ext.toLowerCase()); + if (realMimeType == null) { + realMimeType = message.messageOwner.media.document.mime_type; + if (realMimeType == null || realMimeType.length() == 0) { + realMimeType = null; + } + } + if (realMimeType != null) { + intent.setDataAndType(Uri.fromFile(f), realMimeType); + } else { + intent.setDataAndType(Uri.fromFile(f), "text/plain"); + } + } else { + intent.setDataAndType(Uri.fromFile(f), "text/plain"); + } + } + if (realMimeType != null) { + try { + getParentActivity().startActivityForResult(intent, 500); + } catch (Exception e) { + intent.setDataAndType(Uri.fromFile(f), "text/plain"); + getParentActivity().startActivityForResult(intent, 500); + } + } else { + getParentActivity().startActivityForResult(intent, 500); + } + } catch (Exception e) { + alertUserOpenError(message); + } + } + } + } + + @Override + public void didPressedOther(ChatMediaCell cell) { + createMenu(cell, true); + } + }); + } else if (view instanceof ChatContactCell) { + ((ChatContactCell) view).setContactDelegate(new ChatContactCell.ChatContactCellDelegate() { + @Override + public void didClickAddButton(ChatContactCell cell, TLRPC.User user) { + if (actionBar.isActionModeShowed()) { + processRowSelect(cell); + return; + } + MessageObject messageObject = cell.getMessageObject(); + Bundle args = new Bundle(); + args.putInt("user_id", messageObject.messageOwner.media.user_id); + args.putString("phone", messageObject.messageOwner.media.phone_number); + args.putBoolean("addContact", true); + presentFragment(new ContactAddActivity(args)); + } + + @Override + public void didClickPhone(ChatContactCell cell) { + if (actionBar.isActionModeShowed()) { + processRowSelect(cell); + return; + } + final MessageObject messageObject = cell.getMessageObject(); + if (getParentActivity() == null || messageObject.messageOwner.media.phone_number == null || messageObject.messageOwner.media.phone_number.length() == 0) { + return; + } + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + builder.setItems(new CharSequence[]{LocaleController.getString("Copy", R.string.Copy), LocaleController.getString("Call", R.string.Call)}, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + if (i == 1) { + try { + Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + messageObject.messageOwner.media.phone_number)); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + getParentActivity().startActivityForResult(intent, 500); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } else if (i == 0) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { + android.text.ClipboardManager clipboard = (android.text.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE); + clipboard.setText(messageObject.messageOwner.media.phone_number); + } else { + android.content.ClipboardManager clipboard = (android.content.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE); + android.content.ClipData clip = android.content.ClipData.newPlainText("label", messageObject.messageOwner.media.phone_number); + clipboard.setPrimaryClip(clip); + } + } + } + } + ); + showDialog(builder.create()); + } + }); + } + } else if (view instanceof ChatActionCell) { + ((ChatActionCell) view).setDelegate(new ChatActionCell.ChatActionCellDelegate() { + @Override + public void didClickedImage(ChatActionCell cell) { + MessageObject message = cell.getMessageObject(); + PhotoViewer.getInstance().setParentActivity(getParentActivity()); + PhotoViewer.getInstance().openPhoto(message, ChatActivity.this); + } + + @Override + public void didLongPressed(ChatActionCell cell) { + createMenu(cell, false); + } + + @Override + public void needOpenUserProfile(int uid) { + if (uid != UserConfig.getClientUserId()) { + Bundle args = new Bundle(); + args.putInt("user_id", uid); + presentFragment(new ProfileActivity(args)); + } + } + }); + } + } + + boolean selected = false; + boolean disableSelection = false; + if (actionBar.isActionModeShowed()) { + if (selectedMessagesIds.containsKey(message.getId())) { + view.setBackgroundColor(0x6633b5e5); + selected = true; + } else { + view.setBackgroundColor(0); + } + disableSelection = true; + } else { + view.setBackgroundColor(0); + } + + if (view instanceof ChatBaseCell) { + ChatBaseCell baseCell = (ChatBaseCell) view; + baseCell.isChat = currentChat != null; + baseCell.setMessageObject(message); + baseCell.setCheckPressed(!disableSelection, disableSelection && selected); + if (view instanceof ChatAudioCell && MediaController.getInstance().canDownloadMedia(MediaController.AUTODOWNLOAD_MASK_AUDIO)) { + ((ChatAudioCell) view).downloadAudioIfNeed(); + } + baseCell.setHighlighted(highlightMessageId != Integer.MAX_VALUE && message.getId() == highlightMessageId); + } else if (view instanceof ChatActionCell) { + ChatActionCell actionCell = (ChatActionCell) view; + actionCell.setMessageObject(message); + } + if (type == 6) { + TextView messageTextView = (TextView) view.findViewById(R.id.chat_message_text); + messageTextView.setText(LocaleController.formatPluralString("NewMessages", unread_to_load)); + } + + return view; + } + + @Override + public int getItemViewType(int i) { + int offset = 1; + if (!endReached && messages.size() != 0) { + offset = 0; + if (i == 0) { + return 5; + } + } + if (!forward_end_reached && i == (messages.size() + 1 - offset)) { + return 5; + } + MessageObject message = messages.get(messages.size() - i - offset); + return message.contentType; + } + + @Override + public int getViewTypeCount() { + return 7; + } + + @Override + public boolean isEmpty() { + int count = messages.size(); + if (count != 0) { + if (!endReached) { + count++; + } + if (!forward_end_reached) { + count++; + } + } + return count == 0; + }*/ +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ContactsAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ContactsAdapter.java index cd50fac42..147ff456b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ContactsAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ContactsAdapter.java @@ -19,7 +19,7 @@ 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.android.AnimationCompat.ViewProxy; import org.telegram.ui.Cells.DividerCell; import org.telegram.ui.Cells.GreySectionCell; import org.telegram.ui.Cells.LetterSectionCell; @@ -37,12 +37,14 @@ public class ContactsAdapter extends BaseSectionsAdapter { private HashMap ignoreUsers; private HashMap checkedMap; private boolean scrolling; + private boolean isAdmin; - public ContactsAdapter(Context context, boolean arg1, boolean arg2, HashMap arg3) { + public ContactsAdapter(Context context, boolean arg1, boolean arg2, HashMap arg3, boolean arg4) { mContext = context; onlyUsers = arg1; needPhonebook = arg2; ignoreUsers = arg3; + isAdmin = arg4; } public void setCheckedMap(HashMap map) { @@ -55,7 +57,7 @@ public class ContactsAdapter extends BaseSectionsAdapter { @Override public Object getItem(int section, int position) { - if (onlyUsers) { + if (onlyUsers && !isAdmin) { if (section < ContactsController.getInstance().sortedUsersSectionsArray.size()) { ArrayList arr = ContactsController.getInstance().usersSectionsDict.get(ContactsController.getInstance().sortedUsersSectionsArray.get(section)); if (position < arr.size()) { @@ -84,12 +86,12 @@ public class ContactsAdapter extends BaseSectionsAdapter { @Override public boolean isRowEnabled(int section, int row) { - if (onlyUsers) { + if (onlyUsers && !isAdmin) { ArrayList arr = ContactsController.getInstance().usersSectionsDict.get(ContactsController.getInstance().sortedUsersSectionsArray.get(section)); return row < arr.size(); } else { if (section == 0) { - if (needPhonebook) { + if (needPhonebook || isAdmin) { if (row == 1) { return false; } @@ -113,6 +115,9 @@ public class ContactsAdapter extends BaseSectionsAdapter { if (!onlyUsers) { count++; } + if (isAdmin) { + count++; + } if (needPhonebook) { count++; } @@ -121,7 +126,7 @@ public class ContactsAdapter extends BaseSectionsAdapter { @Override public int getCountForSection(int section) { - if (onlyUsers) { + if (onlyUsers && !isAdmin) { if (section < ContactsController.getInstance().sortedUsersSectionsArray.size()) { ArrayList arr = ContactsController.getInstance().usersSectionsDict.get(ContactsController.getInstance().sortedUsersSectionsArray.get(section)); int count = arr.size(); @@ -132,7 +137,7 @@ public class ContactsAdapter extends BaseSectionsAdapter { } } else { if (section == 0) { - if (needPhonebook) { + if (needPhonebook || isAdmin) { return 2; } else { return 4; @@ -157,7 +162,7 @@ public class ContactsAdapter extends BaseSectionsAdapter { if (convertView == null) { convertView = new LetterSectionCell(mContext); } - if (onlyUsers) { + if (onlyUsers && !isAdmin) { if (section < ContactsController.getInstance().sortedUsersSectionsArray.size()) { ((LetterSectionCell) convertView).setLetter(ContactsController.getInstance().sortedUsersSectionsArray.get(section)); } else { @@ -195,6 +200,8 @@ public class ContactsAdapter extends BaseSectionsAdapter { TextCell actionCell = (TextCell) convertView; if (needPhonebook) { actionCell.setTextAndIcon(LocaleController.getString("InviteFriends", R.string.InviteFriends), R.drawable.menu_invite); + } else if (isAdmin) { + actionCell.setTextAndIcon(LocaleController.getString("InviteToGroupByLink", R.string.InviteToGroupByLink), R.drawable.menu_invite); } else { if (position == 0) { actionCell.setTextAndIcon(LocaleController.getString("NewGroup", R.string.NewGroup), R.drawable.menu_newgroup); @@ -222,7 +229,7 @@ public class ContactsAdapter extends BaseSectionsAdapter { ((UserCell) convertView).setStatusColors(0xffa8a8a8, 0xff3b84c0); } - ArrayList arr = ContactsController.getInstance().usersSectionsDict.get(ContactsController.getInstance().sortedUsersSectionsArray.get(section - (onlyUsers ? 0 : 1))); + ArrayList arr = ContactsController.getInstance().usersSectionsDict.get(ContactsController.getInstance().sortedUsersSectionsArray.get(section - (onlyUsers && !isAdmin ? 0 : 1))); TLRPC.User user = MessagesController.getInstance().getUser(arr.get(position).user_id); ((UserCell)convertView).setData(user, null, null, 0); if (checkedMap != null) { @@ -241,12 +248,12 @@ public class ContactsAdapter extends BaseSectionsAdapter { @Override public int getItemViewType(int section, int position) { - if (onlyUsers) { + if (onlyUsers && !isAdmin) { ArrayList arr = ContactsController.getInstance().usersSectionsDict.get(ContactsController.getInstance().sortedUsersSectionsArray.get(section)); return position < arr.size() ? 0 : 4; } else { if (section == 0) { - if (needPhonebook) { + if (needPhonebook || isAdmin) { if (position == 1) { return 3; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsAdapter.java index e8884689c..56508d07a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsAdapter.java @@ -14,17 +14,25 @@ import android.view.ViewGroup; import org.telegram.android.AndroidUtilities; import org.telegram.android.MessagesController; +import org.telegram.android.support.widget.RecyclerView; import org.telegram.messenger.TLRPC; import org.telegram.ui.Cells.DialogCell; import org.telegram.ui.Cells.LoadingCell; -public class DialogsAdapter extends BaseFragmentAdapter { +public class DialogsAdapter extends RecyclerView.Adapter { private Context mContext; private boolean serverOnly; private long openedDialogId; private int currentCount; + private class Holder extends RecyclerView.ViewHolder { + + public Holder(View itemView) { + super(itemView); + } + } + public DialogsAdapter(Context context, boolean onlyFromServer) { mContext = context; serverOnly = onlyFromServer; @@ -36,21 +44,11 @@ public class DialogsAdapter extends BaseFragmentAdapter { public boolean isDataSetChanged() { int current = currentCount; - return current != getCount(); + return current != getItemCount(); } @Override - public boolean areAllItemsEnabled() { - return true; - } - - @Override - public boolean isEnabled(int i) { - return true; - } - - @Override - public int getCount() { + public int getItemCount() { int count; if (serverOnly) { count = MessagesController.getInstance().dialogsServerOnly.size(); @@ -67,7 +65,6 @@ public class DialogsAdapter extends BaseFragmentAdapter { return count; } - @Override public TLRPC.TL_dialog getItem(int i) { if (serverOnly) { if (i < 0 || i >= MessagesController.getInstance().dialogsServerOnly.size()) { @@ -88,41 +85,32 @@ public class DialogsAdapter extends BaseFragmentAdapter { } @Override - public boolean hasStableIds() { - return true; + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { + View view = null; + if (viewType == 0) { + view = new DialogCell(mContext); + } else if (viewType == 1) { + view = new LoadingCell(mContext); + } + return new Holder(view); } @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); - } - if (view instanceof DialogCell) { //TODO finally i need to find this crash - ((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); - } - } + public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) { + if (viewHolder.getItemViewType() == 0) { + DialogCell cell = (DialogCell) viewHolder.itemView; + cell.useSeparator = (i != getItemCount() - 1); + TLRPC.TL_dialog dialog; + if (serverOnly) { + dialog = MessagesController.getInstance().dialogsServerOnly.get(i); + } else { + dialog = MessagesController.getInstance().dialogs.get(i); + if (AndroidUtilities.isTablet()) { + cell.setDialogSelected(dialog.id == openedDialogId); } - ((DialogCell) view).setDialog(dialog, i, serverOnly); } + cell.setDialog(dialog, i, serverOnly); } - - return view; } @Override @@ -132,26 +120,4 @@ public class DialogsAdapter extends BaseFragmentAdapter { } return 0; } - - @Override - public int getViewTypeCount() { - return 2; - } - - @Override - public boolean isEmpty() { - 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; - } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java index 9a426aeb2..0aae2b086 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java @@ -20,16 +20,14 @@ import org.telegram.android.LocaleController; import org.telegram.android.MessageObject; import org.telegram.android.MessagesController; import org.telegram.android.MessagesStorage; +import org.telegram.android.support.widget.RecyclerView; 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.HashtagSearchCell; @@ -44,7 +42,7 @@ import java.util.Locale; import java.util.Timer; import java.util.TimerTask; -public class DialogsSearchAdapter extends BaseSearchAdapter { +public class DialogsSearchAdapter extends BaseSearchAdapterRecycler { private Context mContext; private Timer searchTimer; @@ -61,6 +59,13 @@ public class DialogsSearchAdapter extends BaseSearchAdapter { private String lastMessagesSearchString; private int lastSearchId = 0; + private class Holder extends RecyclerView.ViewHolder { + + public Holder(View itemView) { + super(itemView); + } + } + private class DialogSearchResult { public TLObject object; public int date; @@ -242,20 +247,18 @@ public class DialogsSearchAdapter extends BaseSearchAdapter { 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++; + TLRPC.User user = TLRPC.User.TLdeserialize(data, data.readInt32(false), false); + DialogSearchResult dialogSearchResult = dialogsResult.get((long) user.id); + if (user.status != null) { + user.status.expires = cursor.intValue(1); } + if (found == 1) { + dialogSearchResult.name = AndroidUtilities.generateSearchName(user.first_name, user.last_name, q); + } else { + dialogSearchResult.name = AndroidUtilities.generateSearchName("@" + user.username, null, "@" + q); + } + dialogSearchResult.object = user; + resultCount++; } MessagesStorage.getInstance().getBuffersStorage().reuseFreeBuffer(data); break; @@ -277,7 +280,7 @@ public class DialogsSearchAdapter extends BaseSearchAdapter { if (name.startsWith(q) || name.contains(" " + q) || tName != null && (tName.startsWith(q) || tName.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()); + TLRPC.Chat chat = TLRPC.Chat.TLdeserialize(data, data.readInt32(false), false); long dialog_id; if (chat.id > 0) { dialog_id = -chat.id; @@ -285,7 +288,7 @@ public class DialogsSearchAdapter extends BaseSearchAdapter { dialog_id = AndroidUtilities.makeBroadcastId(chat.id); } DialogSearchResult dialogSearchResult = dialogsResult.get(dialog_id); - dialogSearchResult.name = Utilities.generateSearchName(chat.title, null, q); + dialogSearchResult.name = AndroidUtilities.generateSearchName(chat.title, null, q); dialogSearchResult.object = chat; resultCount++; } @@ -323,7 +326,7 @@ public class DialogsSearchAdapter extends BaseSearchAdapter { 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()); + TLRPC.EncryptedChat chat = TLRPC.EncryptedChat.TLdeserialize(data, data.readInt32(false), false); DialogSearchResult dialogSearchResult = dialogsResult.get((long) chat.id << 32); chat.user_id = cursor.intValue(2); @@ -342,14 +345,14 @@ public class DialogsSearchAdapter extends BaseSearchAdapter { chat.future_auth_key = cursor.byteArrayValue(15); chat.key_hash = cursor.byteArrayValue(16); - TLRPC.User user = (TLRPC.User) TLClassStore.Instance().TLdeserialize(data2, data2.readInt32()); + TLRPC.User user = TLRPC.User.TLdeserialize(data2, data2.readInt32(false), false); if (user.status != null) { user.status.expires = cursor.intValue(7); } if (found == 1) { dialogSearchResult.name = AndroidUtilities.replaceTags("" + ContactsController.formatName(user.first_name, user.last_name) + ""); } else { - dialogSearchResult.name = Utilities.generateSearchName("@" + user.username, null, "@" + q); + dialogSearchResult.name = AndroidUtilities.generateSearchName("@" + user.username, null, "@" + q); } dialogSearchResult.object = chat; encUsers.add(user); @@ -417,18 +420,16 @@ public class DialogsSearchAdapter extends BaseSearchAdapter { 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); + TLRPC.User user = TLRPC.User.TLdeserialize(data, data.readInt32(false), false); + if (user.status != null) { + user.status.expires = cursor.intValue(1); } + if (found == 1) { + resultArrayNames.add(AndroidUtilities.generateSearchName(user.first_name, user.last_name, q)); + } else { + resultArrayNames.add(AndroidUtilities.generateSearchName("@" + user.username, null, "@" + q)); + } + resultArray.add(user); } MessagesStorage.getInstance().getBuffersStorage().reuseFreeBuffer(data); break; @@ -573,30 +574,7 @@ public class DialogsSearchAdapter extends BaseSearchAdapter { } @Override - public boolean areAllItemsEnabled() { - return false; - } - - @Override - public boolean isEnabled(int i) { - if (!searchResultHashtags.isEmpty()) { - return i != 0; - } - 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 true; - } else if (i > globalCount + localCount && i < globalCount + localCount + messagesCount) { - return true; - } else if (messagesCount != 0 && i == globalCount + localCount + messagesCount) { - return true; - } - return false; - } - - @Override - public int getCount() { + public int getItemCount() { if (!searchResultHashtags.isEmpty()) { return searchResultHashtags.size() + 1; } @@ -612,7 +590,6 @@ public class DialogsSearchAdapter extends BaseSearchAdapter { return count; } - @Override public Object getItem(int i) { if (!searchResultHashtags.isEmpty()) { return searchResultHashtags.get(i - 1); @@ -636,96 +613,107 @@ public class DialogsSearchAdapter extends BaseSearchAdapter { } @Override - public boolean hasStableIds() { - return true; + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = null; + switch (viewType) { + case 0: + view = new ProfileSearchCell(mContext); + view.setBackgroundResource(R.drawable.list_selector); + break; + case 1: + view = new GreySectionCell(mContext); + break; + case 2: + view = new DialogCell(mContext); + break; + case 3: + view = new LoadingCell(mContext); + break; + case 4: + view = new HashtagSearchCell(mContext); + break; + } + return new Holder(view); } @Override - public View getView(int i, View view, ViewGroup viewGroup) { - int type = getItemViewType(i); + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + switch (holder.getItemViewType()) { + case 0: { + ProfileSearchCell cell = (ProfileSearchCell) holder.itemView; - if (type == 1) { - if (view == null) { - view = new GreySectionCell(mContext); - } - if (!searchResultHashtags.isEmpty()) { - ((GreySectionCell) view).setText(LocaleController.getString("Hashtags", R.string.Hashtags).toUpperCase()); - } else 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; - TLRPC.User user = null; - TLRPC.Chat chat = null; - TLRPC.EncryptedChat encryptedChat = null; + int localCount = searchResult.size(); + int globalCount = globalSearch.isEmpty() ? 0 : globalSearch.size() + 1; - 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) { + cell.useSeparator = (position != getItemCount() - 1 && position != localCount - 1 && position != localCount + globalCount - 1); + Object obj = getItem(position); + if (obj instanceof TLRPC.User) { user = (TLRPC.User) obj; - }*/ - 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); - } + } 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; + CharSequence username = null; + CharSequence name = null; + if (position < searchResult.size()) { + name = searchResultNames.get(position); + if (name != null && user != null && user.username != null && user.username.length() > 0) { + if (name.toString().startsWith("@" + user.username)) { + username = name; + name = null; + } + } + } else if (position > searchResult.size() && user != null && user.username != null) { + String foundUserName = lastFoundUsername; + if (foundUserName.startsWith("@")) { + foundUserName = foundUserName.substring(1); + } + try { + username = AndroidUtilities.replaceTags(String.format("@%s%s", user.username.substring(0, foundUserName.length()), user.username.substring(foundUserName.length()))); + } catch (Exception e) { + username = user.username; + FileLog.e("tmessages", e); } } - } else if (i > searchResult.size() && user != null && user.username != null) { - String foundUserName = lastFoundUsername; - if (foundUserName.startsWith("@")) { - foundUserName = foundUserName.substring(1); - } - try { - username = AndroidUtilities.replaceTags(String.format("@%s%s", user.username.substring(0, foundUserName.length()), user.username.substring(foundUserName.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); + cell.setData(user, chat, encryptedChat, name, username); + break; } - ((DialogCell) view).useSeparator = (i != getCount() - 1); - MessageObject messageObject = (MessageObject)getItem(i); - ((DialogCell) view).setDialog(messageObject.getDialogId(), messageObject, messageObject.messageOwner.date); - } else if (type == 3) { - if (view == null) { - view = new LoadingCell(mContext); + case 1: { + GreySectionCell cell = (GreySectionCell) holder.itemView; + if (!searchResultHashtags.isEmpty()) { + cell.setText(LocaleController.getString("Hashtags", R.string.Hashtags).toUpperCase()); + } else if (!globalSearch.isEmpty() && position == searchResult.size()) { + cell.setText(LocaleController.getString("GlobalSearch", R.string.GlobalSearch)); + } else { + cell.setText(LocaleController.getString("SearchMessages", R.string.SearchMessages)); + } + break; } - } else if (type == 4) { - if (view == null) { - view = new HashtagSearchCell(mContext); + case 2: { + DialogCell cell = (DialogCell) holder.itemView; + cell.useSeparator = (position != getItemCount() - 1); + MessageObject messageObject = (MessageObject)getItem(position); + cell.setDialog(messageObject.getDialogId(), messageObject, messageObject.messageOwner.date); + break; + } + case 3: { + break; + } + case 4: { + HashtagSearchCell cell = (HashtagSearchCell) holder.itemView; + cell.setText(searchResultHashtags.get(position - 1)); + cell.setNeedDivider(position != searchResultHashtags.size()); + break; } - ((HashtagSearchCell) view).setText(searchResultHashtags.get(i - 1)); - ((HashtagSearchCell) view).setNeedDivider(i != searchResultHashtags.size()); } - - return view; } @Override @@ -745,14 +733,4 @@ public class DialogsSearchAdapter extends BaseSearchAdapter { } return 1; } - - @Override - public int getViewTypeCount() { - return 5; - } - - @Override - public boolean isEmpty() { - return searchResult.isEmpty() && globalSearch.isEmpty() && searchResultMessages.isEmpty() && searchResultHashtags.isEmpty(); - } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/LocationActivityAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/LocationActivityAdapter.java new file mode 100644 index 000000000..73d7686c1 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/LocationActivityAdapter.java @@ -0,0 +1,167 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Adapters; + +import android.content.Context; +import android.location.Location; +import android.view.View; +import android.view.ViewGroup; + +import org.telegram.android.LocaleController; +import org.telegram.messenger.R; +import org.telegram.messenger.TLRPC; +import org.telegram.ui.Cells.EmptyCell; +import org.telegram.ui.Cells.GreySectionCell; +import org.telegram.ui.Cells.LocationCell; +import org.telegram.ui.Cells.LocationLoadingCell; +import org.telegram.ui.Cells.LocationPoweredCell; +import org.telegram.ui.Cells.SendLocationCell; + +import java.util.Locale; + +public class LocationActivityAdapter extends BaseLocationAdapter { + + private Context mContext; + private int overScrollHeight; + private SendLocationCell sendLocationCell; + private Location gpsLocation; + private Location customLocation; + + public LocationActivityAdapter(Context context) { + super(); + mContext = context; + } + + public void setOverScrollHeight(int value) { + overScrollHeight = value; + } + + public void setGpsLocation(Location location) { + gpsLocation = location; + updateCell(); + } + + public void setCustomLocation(Location location) { + customLocation = location; + updateCell(); + } + + private void updateCell() { + if (sendLocationCell != null) { + if (customLocation != null) { + sendLocationCell.setText(LocaleController.getString("SendSelectedLocation", R.string.SendSelectedLocation), String.format(Locale.US, "(%f,%f)", customLocation.getLatitude(), customLocation.getLongitude())); + } else { + if (gpsLocation != null) { + sendLocationCell.setText(LocaleController.getString("SendLocation", R.string.SendLocation), LocaleController.formatString("AccurateTo", R.string.AccurateTo, LocaleController.formatPluralString("Meters", (int) gpsLocation.getAccuracy()))); + } else { + sendLocationCell.setText(LocaleController.getString("SendLocation", R.string.SendLocation), LocaleController.getString("Loading", R.string.Loading)); + } + } + } + } + + @Override + public int getCount() { + if (searching || !searching && places.isEmpty()) { + return 4; + } + return 3 + places.size() + (places.isEmpty() ? 0 : 1); + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public View getView(int i, View view, ViewGroup viewGroup) { + if (i == 0) { + if (view == null) { + view = new EmptyCell(mContext); + } + ((EmptyCell) view).setHeight(overScrollHeight); + } else if (i == 1) { + if (view == null) { + view = new SendLocationCell(mContext); + } + sendLocationCell = (SendLocationCell) view; + updateCell(); + return view; + } else if (i == 2) { + if (view == null) { + view = new GreySectionCell(mContext); + } + ((GreySectionCell) view).setText(LocaleController.getString("NearbyPlaces", R.string.NearbyPlaces)); + } else if (searching || !searching && places.isEmpty()) { + if (view == null) { + view = new LocationLoadingCell(mContext); + } + ((LocationLoadingCell) view).setLoading(searching); + } else if (i == places.size() + 3) { + if (view == null) { + view = new LocationPoweredCell(mContext); + } + } else { + if (view == null) { + view = new LocationCell(mContext); + } + ((LocationCell) view).setLocation(places.get(i - 3), iconUrls.get(i - 3), true); + } + return view; + } + + @Override + public TLRPC.TL_messageMediaVenue getItem(int i) { + if (i > 2 && i < places.size() + 3) { + return places.get(i - 3); + } + return null; + } + + @Override + public long getItemId(int i) { + return i; + } + + @Override + public int getItemViewType(int position) { + if (position == 0) { + return 0; + } else if (position == 1) { + return 1; + } else if (position == 2) { + return 2; + } else if (searching || !searching && places.isEmpty()) { + return 4; + } else if (position == places.size() + 3) { + return 5; + } + return 3; + } + + @Override + public int getViewTypeCount() { + return 6; + } + + @Override + public boolean areAllItemsEnabled() { + return false; + } + + @Override + public boolean isEnabled(int position) { + return !(position == 2 || position == 0 || position == 3 && (searching || !searching && places.isEmpty()) || position == places.size() + 3); + } + + @Override + public boolean hasStableIds() { + return true; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/LocationActivitySearchAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/LocationActivitySearchAdapter.java new file mode 100644 index 000000000..2da45562e --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/LocationActivitySearchAdapter.java @@ -0,0 +1,83 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Adapters; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; + +import org.telegram.messenger.TLRPC; +import org.telegram.ui.Cells.LocationCell; + +public class LocationActivitySearchAdapter extends BaseLocationAdapter { + + private Context mContext; + + public LocationActivitySearchAdapter(Context context) { + super(); + mContext = context; + } + + @Override + public int getCount() { + return places.size(); + } + + @Override + public boolean isEmpty() { + return places.isEmpty(); + } + + @Override + public View getView(int i, View view, ViewGroup viewGroup) { + if (view == null) { + view = new LocationCell(mContext); + } + ((LocationCell) view).setLocation(places.get(i), iconUrls.get(i), i != places.size() - 1); + return view; + } + + @Override + public TLRPC.TL_messageMediaVenue getItem(int i) { + if (i >= 0 && i < places.size()) { + return places.get(i); + } + return null; + } + + @Override + public long getItemId(int i) { + return i; + } + + @Override + public int getItemViewType(int position) { + return 0; + } + + @Override + public int getViewTypeCount() { + return 4; + } + + @Override + public boolean areAllItemsEnabled() { + return false; + } + + @Override + public boolean isEnabled(int position) { + return true; + } + + @Override + public boolean hasStableIds() { + return true; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MentionsAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MentionsAdapter.java index 6c500386e..312038997 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MentionsAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MentionsAdapter.java @@ -39,10 +39,12 @@ public class MentionsAdapter extends BaseSearchAdapter { private int lastPosition; private ArrayList messages; private boolean needUsernames = true; + private boolean isDarkTheme; - public MentionsAdapter(Context context, MentionsAdapterDelegate delegate) { + public MentionsAdapter(Context context, boolean isDarkTheme, MentionsAdapterDelegate delegate) { mContext = context; this.delegate = delegate; + this.isDarkTheme = isDarkTheme; } public void setChatInfo(TLRPC.ChatParticipants chatParticipants) { @@ -261,6 +263,7 @@ public class MentionsAdapter extends BaseSearchAdapter { public View getView(int i, View view, ViewGroup viewGroup) { if (view == null) { view = new MentionCell(mContext); + ((MentionCell) view).setIsDarkTheme(isDarkTheme); } if (searchResultUsernames != null) { ((MentionCell) view).setUser(searchResultUsernames.get(i)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/SearchAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/SearchAdapter.java index cf7ce7c1f..9894b9592 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/SearchAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/SearchAdapter.java @@ -138,9 +138,9 @@ public class SearchAdapter extends BaseSearchAdapter { if (found != 0) { if (found == 1) { - resultArrayNames.add(Utilities.generateSearchName(user.first_name, user.last_name, q)); + resultArrayNames.add(AndroidUtilities.generateSearchName(user.first_name, user.last_name, q)); } else { - resultArrayNames.add(Utilities.generateSearchName("@" + user.username, null, "@" + q)); + resultArrayNames.add(AndroidUtilities.generateSearchName("@" + user.username, null, "@" + q)); } resultArray.add(user); break; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/StickersAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/StickersAdapter.java index ef6f61c0f..38bb738f5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/StickersAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/StickersAdapter.java @@ -9,24 +9,14 @@ package org.telegram.ui.Adapters; import android.content.Context; -import android.support.v7.widget.RecyclerView; import android.view.View; import android.view.ViewGroup; -import org.telegram.SQLite.SQLiteCursor; -import org.telegram.SQLite.SQLitePreparedStatement; -import org.telegram.android.AndroidUtilities; -import org.telegram.android.MessagesStorage; import org.telegram.android.NotificationCenter; -import org.telegram.messenger.ByteBufferDesc; -import org.telegram.messenger.ConnectionsManager; +import org.telegram.android.query.StickersQuery; +import org.telegram.android.support.widget.RecyclerView; import org.telegram.messenger.FileLoader; -import org.telegram.messenger.FileLog; -import org.telegram.messenger.RPCRequest; -import org.telegram.messenger.TLClassStore; -import org.telegram.messenger.TLObject; import org.telegram.messenger.TLRPC; -import org.telegram.messenger.Utilities; import org.telegram.ui.Cells.StickerCell; import java.io.File; @@ -35,11 +25,6 @@ import java.util.HashMap; public class StickersAdapter extends RecyclerView.Adapter implements NotificationCenter.NotificationCenterDelegate { - private static boolean loadingStickers; - private static String hash = ""; - private static int loadDate = 0; - private static HashMap> allStickers; - private Context mContext; private ArrayList stickers; private ArrayList stickersToLoad = new ArrayList<>(); @@ -61,9 +46,7 @@ public class StickersAdapter extends RecyclerView.Adapter implements Notificatio public StickersAdapter(Context context, StickersAdapterDelegate delegate) { mContext = context; this.delegate = delegate; - if (!loadingStickers && (allStickers == null || loadDate < (System.currentTimeMillis() / 1000 - 60 * 60))) { - loadStickers(true); - } + StickersQuery.checkStickers(); NotificationCenter.getInstance().addObserver(this, NotificationCenter.FileDidLoaded); NotificationCenter.getInstance().addObserver(this, NotificationCenter.FileDidFailedLoad); } @@ -76,64 +59,13 @@ public class StickersAdapter extends RecyclerView.Adapter implements Notificatio @Override public void didReceivedNotification(int id, final Object... args) { if (id == NotificationCenter.FileDidLoaded || id == NotificationCenter.FileDidFailedLoad) { - AndroidUtilities.runOnUIThread(new Runnable() { - @Override - public void run() { - if (stickers != null && !stickers.isEmpty() && !stickersToLoad.isEmpty() && visible) { - String fileName = (String) args[0]; - stickersToLoad.remove(fileName); - if (stickersToLoad.isEmpty()) { - delegate.needChangePanelVisibility(stickers != null && !stickers.isEmpty() && stickersToLoad.isEmpty()); - } - } + if (stickers != null && !stickers.isEmpty() && !stickersToLoad.isEmpty() && visible) { + String fileName = (String) args[0]; + stickersToLoad.remove(fileName); + if (stickersToLoad.isEmpty()) { + delegate.needChangePanelVisibility(stickers != null && !stickers.isEmpty() && stickersToLoad.isEmpty()); } - }); - } - } - - private void loadStickers(boolean cache) { - if (loadingStickers) { - return; - } - loadingStickers = true; - if (cache) { - MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() { - @Override - public void run() { - TLRPC.messages_AllStickers result = null; - int date = 0; - try { - SQLiteCursor cursor = MessagesStorage.getInstance().getDatabase().queryFinalized("SELECT data, date FROM stickers WHERE 1"); - ArrayList loadedUsers = new ArrayList<>(); - if (cursor.next()) { - ByteBufferDesc data = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(cursor.byteArrayLength(0)); - if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { - result = (TLRPC.messages_AllStickers) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); - } - date = cursor.intValue(1); - MessagesStorage.getInstance().getBuffersStorage().reuseFreeBuffer(data); - } - cursor.dispose(); - } catch (Exception e) { - FileLog.e("tmessages", e); - } - processLoadedStickers(result, true, date); - } - }); - } else { - TLRPC.TL_messages_getAllStickers req = new TLRPC.TL_messages_getAllStickers(); - req.hash = hash; - 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() { - processLoadedStickers((TLRPC.messages_AllStickers) response, false, (int)(System.currentTimeMillis() / 1000)); - } - }); - } - }); + } } } @@ -145,109 +77,20 @@ public class StickersAdapter extends RecyclerView.Adapter implements Notificatio int size = Math.min(10, stickers.size()); for (int a = 0; a < size; a++) { TLRPC.Document document = stickers.get(a); - File f = FileLoader.getPathToAttach(document.thumb, true); + File f = FileLoader.getPathToAttach(document.thumb, "webp", true); if (!f.exists()) { - stickersToLoad.add(FileLoader.getAttachFileName(document.thumb)); - FileLoader.getInstance().loadFile(document.thumb.location, 0, true); + stickersToLoad.add(FileLoader.getAttachFileName(document.thumb, "webp")); + FileLoader.getInstance().loadFile(document.thumb.location, "webp", 0, true); } } return stickersToLoad.isEmpty(); } - private void putStickersToCache(final TLRPC.TL_messages_allStickers stickers) { - MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() { - @Override - public void run() { - try { - SQLitePreparedStatement state = MessagesStorage.getInstance().getDatabase().executeFast("REPLACE INTO stickers VALUES(?, ?, ?)"); - state.requery(); - ByteBufferDesc data = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(stickers.getObjectSize()); - stickers.serializeToStream(data); - state.bindInteger(1, 1); - state.bindByteBuffer(2, data.buffer); - state.bindInteger(3, (int) (System.currentTimeMillis() / 1000)); - state.step(); - MessagesStorage.getInstance().getBuffersStorage().reuseFreeBuffer(data); - state.dispose(); - } catch (Exception e) { - FileLog.e("tmessages", e); - } - } - }); - } - - private void processLoadedStickers(final TLRPC.messages_AllStickers res, final boolean cache, final int date) { - AndroidUtilities.runOnUIThread(new Runnable() { - @Override - public void run() { - loadingStickers = false; - } - }); - Utilities.stageQueue.postRunnable(new Runnable() { - @Override - public void run() { - if ((res == null || date < (int) (System.currentTimeMillis() / 1000 - 60 * 60)) && cache) { - AndroidUtilities.runOnUIThread(new Runnable() { - @Override - public void run() { - loadStickers(false); - } - }); - if (res == null) { - return; - } - } - if (res instanceof TLRPC.TL_messages_allStickers) { - if (!cache) { - putStickersToCache((TLRPC.TL_messages_allStickers) res); - } - HashMap documents = new HashMap<>(); - for (TLRPC.Document document : res.documents) { - if (document == null) { - continue; - } - documents.put(document.id, document); - if (document.thumb != null && document.thumb.location != null) { - document.thumb.location.ext = "webp"; - } - } - final HashMap> result = new HashMap<>(); - for (TLRPC.TL_stickerPack stickerPack : res.packs) { - if (stickerPack != null && stickerPack.emoticon != null) { - stickerPack.emoticon = stickerPack.emoticon.replace("\uFE0F", ""); - ArrayList arrayList = result.get(stickerPack.emoticon); - for (Long id : stickerPack.documents) { - TLRPC.Document document = documents.get(id); - if (document != null) { - if (arrayList == null) { - arrayList = new ArrayList<>(); - result.put(stickerPack.emoticon, arrayList); - } - arrayList.add(document); - } - } - } - } - AndroidUtilities.runOnUIThread(new Runnable() { - @Override - public void run() { - allStickers = result; - hash = res.hash; - loadDate = date; - if (lastSticker != null) { - loadStikersForEmoji(lastSticker); - } - } - }); - } - } - }); - } - public void loadStikersForEmoji(CharSequence emoji) { boolean search = emoji != null && emoji.length() != 0 && emoji.length() <= 2; if (search) { lastSticker = emoji.toString(); + HashMap> allStickers = StickersQuery.getAllStickers(); if (allStickers != null) { ArrayList newStickers = allStickers.get(lastSticker); if (stickers != null && newStickers == null) { @@ -301,7 +144,6 @@ public class StickersAdapter extends RecyclerView.Adapter implements Notificatio @Override public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) { - Holder holder = (Holder) viewHolder; int side = 0; if (i == 0) { if (stickers.size() == 1) { @@ -312,6 +154,6 @@ public class StickersAdapter extends RecyclerView.Adapter implements Notificatio } else if (i == stickers.size() - 1) { side = 1; } - ((StickerCell) holder.itemView).setSticker(stickers.get(i), side); + ((StickerCell) viewHolder.itemView).setSticker(stickers.get(i), side); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/BlockedUsersActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/BlockedUsersActivity.java index 5982e413b..1c1699d13 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/BlockedUsersActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/BlockedUsersActivity.java @@ -36,6 +36,7 @@ import org.telegram.ui.Cells.UserCell; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; public class BlockedUsersActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate, ContactsActivity.ContactsActivityDelegate { @@ -97,12 +98,7 @@ public class BlockedUsersActivity extends BaseFragment implements NotificationCe emptyTextView.setGravity(Gravity.CENTER); emptyTextView.setVisibility(View.INVISIBLE); emptyTextView.setText(LocaleController.getString("NoBlocked", R.string.NoBlocked)); - frameLayout.addView(emptyTextView); - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) emptyTextView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.gravity = Gravity.TOP; - emptyTextView.setLayoutParams(layoutParams); + frameLayout.addView(emptyTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT)); emptyTextView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { @@ -111,19 +107,10 @@ public class BlockedUsersActivity extends BaseFragment implements NotificationCe }); progressView = new FrameLayout(context); - frameLayout.addView(progressView); - layoutParams = (FrameLayout.LayoutParams) progressView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; - progressView.setLayoutParams(layoutParams); + frameLayout.addView(progressView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); ProgressBar progressBar = new ProgressBar(context); - progressView.addView(progressBar); - layoutParams = (FrameLayout.LayoutParams) progressView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.CENTER; - progressView.setLayoutParams(layoutParams); + progressView.addView(progressBar, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); listView = new ListView(context); listView.setEmptyView(emptyTextView); @@ -134,11 +121,7 @@ public class BlockedUsersActivity extends BaseFragment implements NotificationCe if (Build.VERSION.SDK_INT >= 11) { listView.setVerticalScrollbarPosition(LocaleController.isRTL ? ListView.SCROLLBAR_POSITION_LEFT : ListView.SCROLLBAR_POSITION_RIGHT); } - frameLayout.addView(listView); - layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; - listView.setLayoutParams(layoutParams); + frameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override @@ -169,7 +152,7 @@ public class BlockedUsersActivity extends BaseFragment implements NotificationCe } } }); - showAlertDialog(builder); + showDialog(builder.create()); return true; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/AddMemberCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/AddMemberCell.java new file mode 100644 index 000000000..a4cff7ad0 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/AddMemberCell.java @@ -0,0 +1,44 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Cells; + +import android.content.Context; +import android.view.Gravity; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import org.telegram.android.AndroidUtilities; +import org.telegram.android.LocaleController; +import org.telegram.messenger.R; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.SimpleTextView; + +public class AddMemberCell extends FrameLayout { + + public AddMemberCell(Context context) { + super(context); + + ImageView imageView = new ImageView(context); + imageView.setImageResource(R.drawable.addmember); + imageView.setScaleType(ImageView.ScaleType.CENTER); + addView(imageView, LayoutHelper.createFrame(48, 48, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 0 : 68, 8, LocaleController.isRTL ? 68 : 0, 0)); + + SimpleTextView textView = new SimpleTextView(context); + textView.setTextColor(0xff212121); + textView.setTextSize(17); + textView.setText(LocaleController.getString("AddMember", R.string.AddMember)); + textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); + addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 20, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 28 : 129, 22.5f, LocaleController.isRTL ? 129 : 28, 0)); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(64), MeasureSpec.EXACTLY)); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java index 2fcaed609..12c749222 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java @@ -28,9 +28,9 @@ import org.telegram.android.MessagesController; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; -import org.telegram.messenger.R; import org.telegram.messenger.TLRPC; import org.telegram.messenger.UserConfig; +import org.telegram.ui.Components.ResourceLoader; import org.telegram.ui.PhotoViewer; import org.telegram.ui.Components.AvatarDrawable; @@ -42,8 +42,6 @@ public class ChatActionCell extends BaseCell { void needOpenUserProfile(int uid); } - private static Drawable backgroundBlack; - private static Drawable backgroundBlue; private static TextPaint textPaint; private URLSpan pressedLink; @@ -65,10 +63,7 @@ public class ChatActionCell extends BaseCell { public ChatActionCell(Context context) { super(context); - if (backgroundBlack == null) { - backgroundBlack = getResources().getDrawable(R.drawable.system_black); - backgroundBlue = getResources().getDrawable(R.drawable.system_blue); - + if (textPaint == null) { textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); textPaint.setColor(0xffffffff); textPaint.linkColor = 0xffffffff; @@ -103,11 +98,11 @@ public class ChatActionCell extends BaseCell { } avatarDrawable.setInfo(id, null, null, false); if (currentMessageObject.messageOwner.action instanceof TLRPC.TL_messageActionUserUpdatedPhoto) { - imageReceiver.setImage(currentMessageObject.messageOwner.action.newUserPhoto.photo_small, "50_50", avatarDrawable, false); + imageReceiver.setImage(currentMessageObject.messageOwner.action.newUserPhoto.photo_small, "50_50", avatarDrawable, null, false); } else { TLRPC.PhotoSize photo = FileLoader.getClosestPhotoSizeWithSize(currentMessageObject.photoThumbs, AndroidUtilities.dp(64)); if (photo != null) { - imageReceiver.setImage(photo.location, "50_50", avatarDrawable, false); + imageReceiver.setImage(photo.location, "50_50", avatarDrawable, null, false); } else { imageReceiver.setImageBitmap(avatarDrawable); } @@ -230,7 +225,7 @@ public class ChatActionCell extends BaseCell { try { int linesCount = textLayout.getLineCount(); for (int a = 0; a < linesCount; a++) { - float lineWidth = 0; + float lineWidth; float lineLeft = 0; try { lineWidth = textLayout.getLineWidth(a); @@ -262,11 +257,11 @@ public class ChatActionCell extends BaseCell { return; } - Drawable backgroundDrawable = null; + Drawable backgroundDrawable; if (ApplicationLoader.isCustomTheme()) { - backgroundDrawable = backgroundBlack; + backgroundDrawable = ResourceLoader.backgroundBlack; } else { - backgroundDrawable = backgroundBlue; + backgroundDrawable = ResourceLoader.backgroundBlue; } backgroundDrawable.setBounds(textX - AndroidUtilities.dp(5), AndroidUtilities.dp(5), textX + textWidth + AndroidUtilities.dp(5), AndroidUtilities.dp(9) + textHeight); backgroundDrawable.draw(canvas); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatAudioCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatAudioCell.java index 7a3f1a6b4..66862c623 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatAudioCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatAudioCell.java @@ -10,6 +10,7 @@ package org.telegram.ui.Cells; import android.content.Context; import android.graphics.Canvas; +import android.graphics.Paint; import android.graphics.drawable.Drawable; import android.text.Layout; import android.text.StaticLayout; @@ -19,27 +20,21 @@ import android.view.SoundEffectConstants; import org.telegram.android.AndroidUtilities; import org.telegram.android.ImageLoader; +import org.telegram.android.MessagesController; import org.telegram.messenger.FileLoader; import org.telegram.android.MediaController; -import org.telegram.messenger.TLRPC; -import org.telegram.android.MessagesController; -import org.telegram.messenger.R; import org.telegram.android.MessageObject; -import org.telegram.android.ImageReceiver; -import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.ProgressView; +import org.telegram.ui.Components.ResourceLoader; import org.telegram.ui.Components.SeekBar; import java.io.File; public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelegate, MediaController.FileDownloadProgressListener { - private static Drawable[][] statesDrawable = new Drawable[8][2]; private static TextPaint timePaint; + private static Paint circlePaint; - private ImageReceiver avatarImage; - private AvatarDrawable avatarDrawable; - private boolean needAvatarImage = false; private SeekBar seekBar; private ProgressView progressView; private int seekBarX; @@ -50,62 +45,42 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega private int buttonY; private boolean buttonPressed = false; - private boolean avatarPressed = false; - private StaticLayout timeLayout; private int timeX; + private int timeWidth; private String lastTimeString = null; private int TAG; - public TLRPC.User audioUser; - private TLRPC.FileLocation currentPhoto; - public ChatAudioCell(Context context) { super(context); TAG = MediaController.getInstance().generateObserverTag(); - avatarImage = new ImageReceiver(this); - avatarImage.setRoundRadius(AndroidUtilities.dp(25)); seekBar = new SeekBar(context); seekBar.delegate = this; progressView = new ProgressView(); - avatarDrawable = new AvatarDrawable(); + drawForwardedName = true; if (timePaint == null) { - statesDrawable[0][0] = getResources().getDrawable(R.drawable.play1); - statesDrawable[0][1] = getResources().getDrawable(R.drawable.play1_pressed); - statesDrawable[1][0] = getResources().getDrawable(R.drawable.pause1); - statesDrawable[1][1] = getResources().getDrawable(R.drawable.pause1_pressed); - statesDrawable[2][0] = getResources().getDrawable(R.drawable.audioload1); - statesDrawable[2][1] = getResources().getDrawable(R.drawable.audioload1_pressed); - statesDrawable[3][0] = getResources().getDrawable(R.drawable.audiocancel1); - statesDrawable[3][1] = getResources().getDrawable(R.drawable.audiocancel1_pressed); - - statesDrawable[4][0] = getResources().getDrawable(R.drawable.play2); - statesDrawable[4][1] = getResources().getDrawable(R.drawable.play2_pressed); - statesDrawable[5][0] = getResources().getDrawable(R.drawable.pause2); - statesDrawable[5][1] = getResources().getDrawable(R.drawable.pause2_pressed); - statesDrawable[6][0] = getResources().getDrawable(R.drawable.audioload2); - statesDrawable[6][1] = getResources().getDrawable(R.drawable.audioload2_pressed); - statesDrawable[7][0] = getResources().getDrawable(R.drawable.audiocancel2); - statesDrawable[7][1] = getResources().getDrawable(R.drawable.audiocancel2_pressed); - timePaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG); timePaint.setTextSize(AndroidUtilities.dp(12)); + + circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG); } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - if (avatarImage != null) { - avatarImage.clearImage(); - currentPhoto = null; - } MediaController.getInstance().removeLoadingFileObserver(this); } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + updateButtonState(); + } + @Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); @@ -123,9 +98,6 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega buttonPressed = true; invalidate(); result = true; - } else if (needAvatarImage && avatarImage.isInsideImage(x, y)) { - avatarPressed = true; - result = true; } } else if (buttonPressed) { if (event.getAction() == MotionEvent.ACTION_UP) { @@ -142,20 +114,6 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega invalidate(); } } - } else if (avatarPressed) { - if (event.getAction() == MotionEvent.ACTION_UP) { - avatarPressed = false; - playSoundEffect(SoundEffectConstants.CLICK); - if (delegate != null) { - delegate.didPressedUserAvatar(this, audioUser); - } - } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { - avatarPressed = false; - } else if (event.getAction() == MotionEvent.ACTION_MOVE) { - if (!avatarImage.isInsideImage(x, y)) { - avatarPressed = false; - } - } } if (!result) { result = super.onTouchEvent(event); @@ -168,6 +126,9 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega private void didPressedButton() { if (buttonState == 0) { boolean result = MediaController.getInstance().playAudio(currentMessageObject); + if (!currentMessageObject.isOut() && currentMessageObject.isContentUnread()) { + MessagesController.getInstance().markMessageContentAsRead(currentMessageObject.getId()); + } if (result) { buttonState = 1; invalidate(); @@ -204,7 +165,7 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega seekBar.setProgress(currentMessageObject.audioProgress); } - int duration = 0; + int duration; if (!MediaController.getInstance().isPlayingAudio(currentMessageObject)) { duration = currentMessageObject.messageOwner.media.audio.duration; } else { @@ -212,7 +173,7 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega } String timeString = String.format("%02d:%02d", duration / 60, duration % 60); if (lastTimeString == null || lastTimeString != null && !lastTimeString.equals(timeString)) { - int timeWidth = (int)Math.ceil(timePaint.measureText(timeString)); + timeWidth = (int)Math.ceil(timePaint.measureText(timeString)); timeLayout = new StaticLayout(timeString, timePaint, timeWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); } invalidate(); @@ -227,29 +188,36 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega } public void updateButtonState() { - String fileName = currentMessageObject.getFileName(); - File cacheFile = FileLoader.getPathToMessage(currentMessageObject.messageOwner); - if (cacheFile.exists()) { - MediaController.getInstance().removeLoadingFileObserver(this); - boolean playing = MediaController.getInstance().isPlayingAudio(currentMessageObject); - if (!playing || playing && MediaController.getInstance().isAudioPaused()) { - buttonState = 0; - } else { - buttonState = 1; - } - progressView.setProgress(0); + if (currentMessageObject == null) { + return; + } + if (currentMessageObject.isOut() && currentMessageObject.isSending()) { + buttonState = 4; } else { - MediaController.getInstance().addLoadingFileObserver(fileName, this); - if (!FileLoader.getInstance().isLoadingFile(fileName)) { - buttonState = 2; + String fileName = currentMessageObject.getFileName(); + File cacheFile = FileLoader.getPathToMessage(currentMessageObject.messageOwner); + if (cacheFile.exists()) { + MediaController.getInstance().removeLoadingFileObserver(this); + boolean playing = MediaController.getInstance().isPlayingAudio(currentMessageObject); + if (!playing || playing && MediaController.getInstance().isAudioPaused()) { + buttonState = 0; + } else { + buttonState = 1; + } progressView.setProgress(0); } else { - buttonState = 3; - Float progress = ImageLoader.getInstance().getFileProgress(fileName); - if (progress != null) { - progressView.setProgress(progress); - } else { + MediaController.getInstance().addLoadingFileObserver(fileName, this); + if (!FileLoader.getInstance().isLoadingFile(fileName)) { + buttonState = 2; progressView.setProgress(0); + } else { + buttonState = 3; + Float progress = ImageLoader.getInstance().getFileProgress(fileName); + if (progress != null) { + progressView.setProgress(progress); + } else { + progressView.setProgress(0); + } } } } @@ -297,65 +265,39 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); - setMeasuredDimension(width, AndroidUtilities.dp(68) + namesOffset); + setMeasuredDimension(width, AndroidUtilities.dp(66) + namesOffset); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - int x; - if (currentMessageObject.isOut()) { - x = layoutWidth - backgroundWidth + AndroidUtilities.dp(8); - seekBarX = layoutWidth - backgroundWidth + AndroidUtilities.dp(97); - buttonX = layoutWidth - backgroundWidth + AndroidUtilities.dp(67); - timeX = layoutWidth - backgroundWidth + AndroidUtilities.dp(71); + seekBarX = layoutWidth - backgroundWidth + AndroidUtilities.dp(55); + buttonX = layoutWidth - backgroundWidth + AndroidUtilities.dp(13); + timeX = layoutWidth - backgroundWidth + AndroidUtilities.dp(66); } else { if (isChat) { - x = AndroidUtilities.dp(69); - seekBarX = AndroidUtilities.dp(158); - buttonX = AndroidUtilities.dp(128); - timeX = AndroidUtilities.dp(132); + seekBarX = AndroidUtilities.dp(116); + buttonX = AndroidUtilities.dp(74); + timeX = AndroidUtilities.dp(127); } else { - x = AndroidUtilities.dp(16); - seekBarX = AndroidUtilities.dp(106); - buttonX = AndroidUtilities.dp(76); - timeX = AndroidUtilities.dp(80); + seekBarX = AndroidUtilities.dp(64); + buttonX = AndroidUtilities.dp(22); + timeX = AndroidUtilities.dp(75); } } - int diff = 0; - if (needAvatarImage) { - avatarImage.setImageCoords(x, AndroidUtilities.dp(9) + namesOffset, AndroidUtilities.dp(50), AndroidUtilities.dp(50)); - } else { - diff = AndroidUtilities.dp(56); - seekBarX -= diff; - buttonX -= diff; - timeX -= diff; - } - seekBar.width = backgroundWidth - AndroidUtilities.dp(112) + diff; + seekBar.width = backgroundWidth - AndroidUtilities.dp(70); seekBar.height = AndroidUtilities.dp(30); - progressView.width = backgroundWidth - AndroidUtilities.dp(136) + diff; + progressView.width = backgroundWidth - AndroidUtilities.dp(94); progressView.height = AndroidUtilities.dp(30); - seekBarY = AndroidUtilities.dp(13) + namesOffset; - buttonY = AndroidUtilities.dp(10) + namesOffset; + seekBarY = AndroidUtilities.dp(11) + namesOffset; + buttonY = AndroidUtilities.dp(13) + namesOffset; updateProgress(); } - @Override - protected boolean isUserDataChanged() { - TLRPC.User newUser = MessagesController.getInstance().getUser(currentMessageObject.messageOwner.media.audio.user_id); - TLRPC.FileLocation newPhoto = null; - - if (avatarImage != null && newUser != null && newUser.photo != null) { - newPhoto = newUser.photo.photo_small; - } - - return currentPhoto == null && newPhoto != null || currentPhoto != null && newPhoto == null || currentPhoto != null && newPhoto != null && (currentPhoto.local_id != newPhoto.local_id || currentPhoto.volume_id != newPhoto.volume_id) || super.isUserDataChanged(); - } - @Override public void setMessageObject(MessageObject messageObject) { if (currentMessageObject != messageObject || isUserDataChanged()) { @@ -365,28 +307,6 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega backgroundWidth = Math.min(AndroidUtilities.displaySize.x - AndroidUtilities.dp(isChat ? 102 : 50), AndroidUtilities.dp(300)); } - int uid = messageObject.messageOwner.media.audio.user_id; - if (uid == 0) { - uid = messageObject.messageOwner.from_id; - } - needAvatarImage = !(messageObject.messageOwner.to_id != null && messageObject.messageOwner.to_id.chat_id != 0 && !messageObject.isOut() && messageObject.messageOwner.media.audio.user_id == messageObject.messageOwner.from_id); - audioUser = MessagesController.getInstance().getUser(uid); - - if (needAvatarImage) { - if (audioUser != null) { - if (audioUser.photo != null) { - currentPhoto = audioUser.photo.photo_small; - } else { - currentPhoto = null; - } - avatarDrawable.setInfo(audioUser); - } else { - avatarDrawable.setInfo(uid, null, null, false); - currentPhoto = null; - } - avatarImage.setImage(currentPhoto, "50_50", avatarDrawable, false); - } - if (messageObject.isOut()) { seekBar.type = 0; progressView.setProgressColors(0xffb4e396, 0xff6ac453); @@ -408,10 +328,6 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega return; } - if (needAvatarImage) { - avatarImage.draw(canvas); - } - canvas.save(); if (buttonState == 0 || buttonState == 1) { canvas.translate(seekBarX, seekBarY); @@ -423,22 +339,25 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega canvas.restore(); int state = buttonState; - if (!currentMessageObject.isOut()) { - state += 4; - timePaint.setColor(0xffa1aab3); - } else { + if (currentMessageObject.isOut()) { timePaint.setColor(0xff70b15c); + circlePaint.setColor(0xff87bf78); + } else { + state += 5; + timePaint.setColor(0xffa1aab3); + circlePaint.setColor(0xff4195e5); } - Drawable buttonDrawable = statesDrawable[state][buttonPressed ? 1 : 0]; - int side = AndroidUtilities.dp(36); - int x = (side - buttonDrawable.getIntrinsicWidth()) / 2; - int y = (side - buttonDrawable.getIntrinsicHeight()) / 2; - setDrawableBounds(buttonDrawable, x + buttonX, y + buttonY); + Drawable buttonDrawable = ResourceLoader.audioStatesDrawable[state][buttonPressed ? 1 : 0]; + setDrawableBounds(buttonDrawable, buttonX, buttonY); buttonDrawable.draw(canvas); canvas.save(); - canvas.translate(timeX, AndroidUtilities.dp(45) + namesOffset); + canvas.translate(timeX, AndroidUtilities.dp(42) + namesOffset); timeLayout.draw(canvas); canvas.restore(); + + if (currentMessageObject.isContentUnread()) { + canvas.drawCircle(timeX + timeWidth + AndroidUtilities.dp(8), AndroidUtilities.dp(49.5f) + namesOffset, AndroidUtilities.dp(3), circlePaint); + } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatBaseCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatBaseCell.java index a56e53678..5b8f96c46 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatBaseCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatBaseCell.java @@ -13,11 +13,13 @@ import android.content.Context; import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.Paint; +import android.graphics.Path; import android.graphics.drawable.Drawable; import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; +import android.text.style.ClickableSpan; import android.view.MotionEvent; import android.view.SoundEffectConstants; @@ -34,6 +36,8 @@ import org.telegram.messenger.R; import org.telegram.android.MessageObject; import org.telegram.android.ImageReceiver; import org.telegram.ui.Components.AvatarDrawable; +import org.telegram.ui.Components.ResourceLoader; +import org.telegram.ui.Components.StaticLayoutEx; public class ChatBaseCell extends BaseCell { @@ -43,9 +47,50 @@ public class ChatBaseCell extends BaseCell { void didLongPressed(ChatBaseCell cell); void didPressReplyMessage(ChatBaseCell cell, int id); void didPressUrl(String url); + void needOpenWebView(String url, String title, String originalUrl, int w, int h); boolean canPerformActions(); } + protected class MyPath extends Path { + + private StaticLayout currentLayout; + private int currentLine; + private float lastTop = -1; + + public void setCurrentLayout(StaticLayout layout, int start) { + currentLayout = layout; + currentLine = layout.getLineForOffset(start); + lastTop = -1; + } + + @Override + public void addRect(float left, float top, float right, float bottom, Direction dir) { + if (lastTop == -1) { + lastTop = top; + } else if (lastTop != top) { + lastTop = top; + currentLine++; + } + float lineRight = currentLayout.getLineRight(currentLine); + float lineLeft = currentLayout.getLineLeft(currentLine); + if (left >= lineRight) { + return; + } + if (right > lineRight) { + right = lineRight; + } + if (left < lineLeft) { + left = lineLeft; + } + super.addRect(left, top, right, bottom, dir); + } + } + + protected ClickableSpan pressedLink; + protected boolean linkPreviewPressed; + protected MyPath urlPath = new MyPath(); + protected static Paint urlPaint; + public boolean isChat = false; protected boolean isPressed = false; protected boolean forwardName = false; @@ -57,26 +102,6 @@ public class ChatBaseCell extends BaseCell { protected boolean drawBackground = true; protected MessageObject currentMessageObject; - private static Drawable backgroundDrawableIn; - private static Drawable backgroundDrawableInSelected; - private static Drawable backgroundDrawableOut; - private static Drawable backgroundDrawableOutSelected; - private static Drawable backgroundMediaDrawableIn; - private static Drawable backgroundMediaDrawableInSelected; - private static Drawable backgroundMediaDrawableOut; - private static Drawable backgroundMediaDrawableOutSelected; - private static Drawable checkDrawable; - private static Drawable halfCheckDrawable; - private static Drawable clockDrawable; - private static Drawable broadcastDrawable; - private static Drawable checkMediaDrawable; - private static Drawable halfCheckMediaDrawable; - private static Drawable clockMediaDrawable; - private static Drawable broadcastMediaDrawable; - private static Drawable errorDrawable; - private static Drawable backgroundBlack; - private static Drawable backgroundBlue; - protected static Drawable mediaBackgroundDrawable; private static TextPaint timePaintIn; private static TextPaint timePaintOut; private static TextPaint timeMediaPaint; @@ -144,28 +169,7 @@ public class ChatBaseCell extends BaseCell { public ChatBaseCell(Context context) { super(context); - if (backgroundDrawableIn == null) { - backgroundDrawableIn = getResources().getDrawable(R.drawable.msg_in); - backgroundDrawableInSelected = getResources().getDrawable(R.drawable.msg_in_selected); - backgroundDrawableOut = getResources().getDrawable(R.drawable.msg_out); - backgroundDrawableOutSelected = getResources().getDrawable(R.drawable.msg_out_selected); - backgroundMediaDrawableIn = getResources().getDrawable(R.drawable.msg_in_photo); - backgroundMediaDrawableInSelected = getResources().getDrawable(R.drawable.msg_in_photo_selected); - backgroundMediaDrawableOut = getResources().getDrawable(R.drawable.msg_out_photo); - backgroundMediaDrawableOutSelected = getResources().getDrawable(R.drawable.msg_out_photo_selected); - checkDrawable = getResources().getDrawable(R.drawable.msg_check); - halfCheckDrawable = getResources().getDrawable(R.drawable.msg_halfcheck); - clockDrawable = getResources().getDrawable(R.drawable.msg_clock); - checkMediaDrawable = getResources().getDrawable(R.drawable.msg_check_w); - halfCheckMediaDrawable = getResources().getDrawable(R.drawable.msg_halfcheck_w); - clockMediaDrawable = getResources().getDrawable(R.drawable.msg_clock_photo); - errorDrawable = getResources().getDrawable(R.drawable.msg_warning); - mediaBackgroundDrawable = getResources().getDrawable(R.drawable.phototime); - broadcastDrawable = getResources().getDrawable(R.drawable.broadcast3); - broadcastMediaDrawable = getResources().getDrawable(R.drawable.broadcast4); - backgroundBlack = getResources().getDrawable(R.drawable.system_black); - backgroundBlue = getResources().getDrawable(R.drawable.system_blue); - + if (timePaintIn == null) { timePaintIn = new TextPaint(TextPaint.ANTI_ALIAS_FLAG); timePaintIn.setTextSize(AndroidUtilities.dp(12)); timePaintIn.setColor(0xffa1aab3); @@ -193,6 +197,9 @@ public class ChatBaseCell extends BaseCell { replyTextPaint.linkColor = 0xff316f9f; replyLinePaint = new Paint(); + + urlPaint = new Paint(); + urlPaint.setColor(0x33316f9f); } avatarImage = new ImageReceiver(this); avatarImage.setRoundRadius(AndroidUtilities.dp(21)); @@ -203,10 +210,15 @@ public class ChatBaseCell extends BaseCell { @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - avatarImage.clearImage(); - replyImageReceiver.clearImage(); - currentPhoto = null; - currentReplyPhoto = null; + avatarImage.onDetachedFromWindow(); + replyImageReceiver.onDetachedFromWindow(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + avatarImage.onAttachedToWindow(); + replyImageReceiver.onAttachedToWindow(); } @Override @@ -215,6 +227,14 @@ public class ChatBaseCell extends BaseCell { invalidate(); } + protected void resetPressedLink() { + if (pressedLink != null) { + pressedLink = null; + } + linkPreviewPressed = false; + invalidate(); + } + public void setDelegate(ChatBaseCellDelegate delegate) { this.delegate = delegate; } @@ -289,6 +309,20 @@ public class ChatBaseCell extends BaseCell { return currentForwardNameString == null && newNameString != null || currentForwardNameString != null && newNameString == null || currentForwardNameString != null && newNameString != null && !currentForwardNameString.equals(newNameString); } + protected void measureTime(MessageObject messageObject) { + if (!media) { + if (messageObject.isOut()) { + currentTimePaint = timePaintOut; + } else { + currentTimePaint = timePaintIn; + } + } else { + currentTimePaint = timeMediaPaint; + } + currentTimeString = LocaleController.formatterDay.format((long) (messageObject.messageOwner.date) * 1000); + timeWidth = (int)Math.ceil(currentTimePaint.measureText(currentTimeString)); + } + public void setMessageObject(MessageObject messageObject) { currentMessageObject = messageObject; last_send_state = messageObject.messageOwner.send_state; @@ -317,7 +351,7 @@ public class ChatBaseCell extends BaseCell { currentPhoto = null; avatarDrawable.setInfo(messageObject.messageOwner.from_id, null, null, false); } - avatarImage.setImage(currentPhoto, "50_50", avatarDrawable, false); + avatarImage.setImage(currentPhoto, "50_50", avatarDrawable, null, false); } if (!media) { @@ -363,7 +397,7 @@ public class ChatBaseCell extends BaseCell { CharSequence str = TextUtils.ellipsize(currentForwardNameString.replace("\n", " "), forwardNamePaint, forwardedNameWidth - AndroidUtilities.dp(40), TextUtils.TruncateAt.END); str = AndroidUtilities.replaceTags(String.format("%s\n%s %s", LocaleController.getString("ForwardedMessage", R.string.ForwardedMessage), LocaleController.getString("From", R.string.From), str)); - forwardedNameLayout = new StaticLayout(str, forwardNamePaint, forwardedNameWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + forwardedNameLayout = StaticLayoutEx.createStaticLayout(str, forwardNamePaint, forwardedNameWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false, TextUtils.TruncateAt.END, forwardedNameWidth, 2); if (forwardedNameLayout.getLineCount() > 1) { forwardedNameWidth = Math.max((int) Math.ceil(forwardedNameLayout.getLineWidth(0)), (int) Math.ceil(forwardedNameLayout.getLineWidth(1))); namesOffset += AndroidUtilities.dp(36); @@ -431,7 +465,7 @@ public class ChatBaseCell extends BaseCell { needReplyImage = false; } else { currentReplyPhoto = photoSize.location; - replyImageReceiver.setImage(photoSize.location, "50_50", null, true); + replyImageReceiver.setImage(photoSize.location, "50_50", null, null, true); needReplyImage = true; maxWidth -= AndroidUtilities.dp(44); } @@ -623,34 +657,34 @@ public class ChatBaseCell extends BaseCell { avatarImage.draw(canvas); } - Drawable currentBackgroundDrawable = null; + Drawable currentBackgroundDrawable; if (currentMessageObject.isOut()) { if (isPressed() && isCheckPressed || !isCheckPressed && isPressed || isHighlighted) { if (!media) { - currentBackgroundDrawable = backgroundDrawableOutSelected; + currentBackgroundDrawable = ResourceLoader.backgroundDrawableOutSelected; } else { - currentBackgroundDrawable = backgroundMediaDrawableOutSelected; + currentBackgroundDrawable = ResourceLoader.backgroundMediaDrawableOutSelected; } } else { if (!media) { - currentBackgroundDrawable = backgroundDrawableOut; + currentBackgroundDrawable = ResourceLoader.backgroundDrawableOut; } else { - currentBackgroundDrawable = backgroundMediaDrawableOut; + currentBackgroundDrawable = ResourceLoader.backgroundMediaDrawableOut; } } setDrawableBounds(currentBackgroundDrawable, layoutWidth - backgroundWidth - (!media ? 0 : AndroidUtilities.dp(9)), AndroidUtilities.dp(1), backgroundWidth, layoutHeight - AndroidUtilities.dp(2)); } else { if (isPressed() && isCheckPressed || !isCheckPressed && isPressed || isHighlighted) { if (!media) { - currentBackgroundDrawable = backgroundDrawableInSelected; + currentBackgroundDrawable = ResourceLoader.backgroundDrawableInSelected; } else { - currentBackgroundDrawable = backgroundMediaDrawableInSelected; + currentBackgroundDrawable = ResourceLoader.backgroundMediaDrawableInSelected; } } else { if (!media) { - currentBackgroundDrawable = backgroundDrawableIn; + currentBackgroundDrawable = ResourceLoader.backgroundDrawableIn; } else { - currentBackgroundDrawable = backgroundMediaDrawableIn; + currentBackgroundDrawable = ResourceLoader.backgroundMediaDrawableIn; } } if (isChat) { @@ -703,9 +737,9 @@ public class ChatBaseCell extends BaseCell { } Drawable back; if (ApplicationLoader.isCustomTheme()) { - back = backgroundBlack; + back = ResourceLoader.backgroundBlack; } else { - back = backgroundBlue; + back = ResourceLoader.backgroundBlue; } replyStartY = layoutHeight - AndroidUtilities.dp(58); back.setBounds(replyStartX - AndroidUtilities.dp(7), replyStartY - AndroidUtilities.dp(6), replyStartX - AndroidUtilities.dp(7) + backWidth, replyStartY + AndroidUtilities.dp(41)); @@ -755,10 +789,10 @@ public class ChatBaseCell extends BaseCell { } } - if (drawTime) { + if (drawTime || !media) { if (media) { - setDrawableBounds(mediaBackgroundDrawable, timeX - AndroidUtilities.dp(3), layoutHeight - AndroidUtilities.dp(27.5f), timeWidth + AndroidUtilities.dp(6 + (currentMessageObject.isOut() ? 20 : 0)), AndroidUtilities.dp(16.5f)); - mediaBackgroundDrawable.draw(canvas); + setDrawableBounds(ResourceLoader.mediaBackgroundDrawable, timeX - AndroidUtilities.dp(3), layoutHeight - AndroidUtilities.dp(27.5f), timeWidth + AndroidUtilities.dp(6 + (currentMessageObject.isOut() ? 20 : 0)), AndroidUtilities.dp(16.5f)); + ResourceLoader.mediaBackgroundDrawable.draw(canvas); canvas.save(); canvas.translate(timeX, layoutHeight - AndroidUtilities.dp(12.0f) - timeLayout.getHeight()); @@ -802,58 +836,58 @@ public class ChatBaseCell extends BaseCell { if (drawClock) { if (!media) { - setDrawableBounds(clockDrawable, layoutWidth - AndroidUtilities.dp(18.5f) - clockDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(8.5f) - clockDrawable.getIntrinsicHeight()); - clockDrawable.draw(canvas); + setDrawableBounds(ResourceLoader.clockDrawable, layoutWidth - AndroidUtilities.dp(18.5f) - ResourceLoader.clockDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(8.5f) - ResourceLoader.clockDrawable.getIntrinsicHeight()); + ResourceLoader.clockDrawable.draw(canvas); } else { - setDrawableBounds(clockMediaDrawable, layoutWidth - AndroidUtilities.dp(22.0f) - clockMediaDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(13.0f) - clockMediaDrawable.getIntrinsicHeight()); - clockMediaDrawable.draw(canvas); + setDrawableBounds(ResourceLoader.clockMediaDrawable, layoutWidth - AndroidUtilities.dp(22.0f) - ResourceLoader.clockMediaDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(13.0f) - ResourceLoader.clockMediaDrawable.getIntrinsicHeight()); + ResourceLoader.clockMediaDrawable.draw(canvas); } } if (isBroadcast) { if (drawCheck1 || drawCheck2) { if (!media) { - setDrawableBounds(broadcastDrawable, layoutWidth - AndroidUtilities.dp(20.5f) - broadcastDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(8.0f) - broadcastDrawable.getIntrinsicHeight()); - broadcastDrawable.draw(canvas); + setDrawableBounds(ResourceLoader.broadcastDrawable, layoutWidth - AndroidUtilities.dp(20.5f) - ResourceLoader.broadcastDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(8.0f) - ResourceLoader.broadcastDrawable.getIntrinsicHeight()); + ResourceLoader.broadcastDrawable.draw(canvas); } else { - setDrawableBounds(broadcastMediaDrawable, layoutWidth - AndroidUtilities.dp(24.0f) - broadcastMediaDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(13.0f) - broadcastMediaDrawable.getIntrinsicHeight()); - broadcastMediaDrawable.draw(canvas); + setDrawableBounds(ResourceLoader.broadcastMediaDrawable, layoutWidth - AndroidUtilities.dp(24.0f) - ResourceLoader.broadcastMediaDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(13.0f) - ResourceLoader.broadcastMediaDrawable.getIntrinsicHeight()); + ResourceLoader.broadcastMediaDrawable.draw(canvas); } } } else { if (drawCheck2) { if (!media) { if (drawCheck1) { - setDrawableBounds(checkDrawable, layoutWidth - AndroidUtilities.dp(22.5f) - checkDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(8.0f) - checkDrawable.getIntrinsicHeight()); + setDrawableBounds(ResourceLoader.checkDrawable, layoutWidth - AndroidUtilities.dp(22.5f) - ResourceLoader.checkDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(8.0f) - ResourceLoader.checkDrawable.getIntrinsicHeight()); } else { - setDrawableBounds(checkDrawable, layoutWidth - AndroidUtilities.dp(18.5f) - checkDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(8.0f) - checkDrawable.getIntrinsicHeight()); + setDrawableBounds(ResourceLoader.checkDrawable, layoutWidth - AndroidUtilities.dp(18.5f) - ResourceLoader.checkDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(8.0f) - ResourceLoader.checkDrawable.getIntrinsicHeight()); } - checkDrawable.draw(canvas); + ResourceLoader.checkDrawable.draw(canvas); } else { if (drawCheck1) { - setDrawableBounds(checkMediaDrawable, layoutWidth - AndroidUtilities.dp(26.0f) - checkMediaDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(13.0f) - checkMediaDrawable.getIntrinsicHeight()); + setDrawableBounds(ResourceLoader.checkMediaDrawable, layoutWidth - AndroidUtilities.dp(26.0f) - ResourceLoader.checkMediaDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(13.0f) - ResourceLoader.checkMediaDrawable.getIntrinsicHeight()); } else { - setDrawableBounds(checkMediaDrawable, layoutWidth - AndroidUtilities.dp(22.0f) - checkMediaDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(13.0f) - checkMediaDrawable.getIntrinsicHeight()); + setDrawableBounds(ResourceLoader.checkMediaDrawable, layoutWidth - AndroidUtilities.dp(22.0f) - ResourceLoader.checkMediaDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(13.0f) - ResourceLoader.checkMediaDrawable.getIntrinsicHeight()); } - checkMediaDrawable.draw(canvas); + ResourceLoader.checkMediaDrawable.draw(canvas); } } if (drawCheck1) { if (!media) { - setDrawableBounds(halfCheckDrawable, layoutWidth - AndroidUtilities.dp(18) - halfCheckDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(8.0f) - halfCheckDrawable.getIntrinsicHeight()); - halfCheckDrawable.draw(canvas); + setDrawableBounds(ResourceLoader.halfCheckDrawable, layoutWidth - AndroidUtilities.dp(18) - ResourceLoader.halfCheckDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(8.0f) - ResourceLoader.halfCheckDrawable.getIntrinsicHeight()); + ResourceLoader.halfCheckDrawable.draw(canvas); } else { - setDrawableBounds(halfCheckMediaDrawable, layoutWidth - AndroidUtilities.dp(20.5f) - halfCheckMediaDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(13.0f) - halfCheckMediaDrawable.getIntrinsicHeight()); - halfCheckMediaDrawable.draw(canvas); + setDrawableBounds(ResourceLoader.halfCheckMediaDrawable, layoutWidth - AndroidUtilities.dp(20.5f) - ResourceLoader.halfCheckMediaDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(13.0f) - ResourceLoader.halfCheckMediaDrawable.getIntrinsicHeight()); + ResourceLoader.halfCheckMediaDrawable.draw(canvas); } } } if (drawError) { if (!media) { - setDrawableBounds(errorDrawable, layoutWidth - AndroidUtilities.dp(18) - errorDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(6.5f) - errorDrawable.getIntrinsicHeight()); - errorDrawable.draw(canvas); + setDrawableBounds(ResourceLoader.errorDrawable, layoutWidth - AndroidUtilities.dp(18) - ResourceLoader.errorDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(6.5f) - ResourceLoader.errorDrawable.getIntrinsicHeight()); + ResourceLoader.errorDrawable.draw(canvas); } else { - setDrawableBounds(errorDrawable, layoutWidth - AndroidUtilities.dp(20.5f) - errorDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(12.5f) - errorDrawable.getIntrinsicHeight()); - errorDrawable.draw(canvas); + setDrawableBounds(ResourceLoader.errorDrawable, layoutWidth - AndroidUtilities.dp(20.5f) - ResourceLoader.errorDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(12.5f) - ResourceLoader.errorDrawable.getIntrinsicHeight()); + ResourceLoader.errorDrawable.draw(canvas); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatContactCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatContactCell.java index 45907c708..129348b60 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatContactCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatContactCell.java @@ -195,7 +195,7 @@ public class ChatContactCell extends ChatBaseCell { currentPhoto = null; avatarDrawable.setInfo(uid, null, null, false); } - avatarImage.setImage(currentPhoto, "50_50", avatarDrawable, false); + avatarImage.setImage(currentPhoto, "50_50", avatarDrawable, null, false); String currentNameString = ContactsController.formatName(messageObject.messageOwner.media.first_name, messageObject.messageOwner.media.last_name); int nameWidth = Math.min((int) Math.ceil(namePaint.measureText(currentNameString)), maxWidth); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMediaCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMediaCell.java index a2a6f9245..df409c4b7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMediaCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMediaCell.java @@ -16,9 +16,11 @@ import android.graphics.RectF; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.text.Layout; +import android.text.Spannable; import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; +import android.text.style.ClickableSpan; import android.view.MotionEvent; import android.view.SoundEffectConstants; @@ -29,11 +31,14 @@ import org.telegram.android.SendMessagesHelper; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.FileLoader; import org.telegram.android.MediaController; +import org.telegram.messenger.FileLog; import org.telegram.messenger.R; import org.telegram.messenger.TLRPC; -import org.telegram.messenger.Utilities; import org.telegram.android.MessageObject; import org.telegram.ui.Components.RadialProgress; +import org.telegram.ui.Components.ResourceLoader; +import org.telegram.ui.Components.StaticLayoutEx; +import org.telegram.ui.Components.URLSpanNoUnderline; import org.telegram.ui.PhotoViewer; import org.telegram.ui.Components.GifDrawable; import org.telegram.android.ImageReceiver; @@ -45,21 +50,17 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD public interface ChatMediaCellDelegate { void didClickedImage(ChatMediaCell cell); + void didPressedOther(ChatMediaCell cell); } - private static Drawable placeholderDocInDrawable; - private static Drawable placeholderDocOutDrawable; - private static Drawable videoIconDrawable; - private static Drawable docMenuInDrawable; - private static Drawable docMenuOutDrawable; - private static Drawable[] buttonStatesDrawables = new Drawable[8]; - private static Drawable[][] buttonStatesDrawablesDoc = new Drawable[3][2]; private static TextPaint infoPaint; private static MessageObject lastDownloadedGifMessage = null; private static TextPaint namePaint; private static Paint docBackPaint; private static Paint deleteProgressPaint; + private static TextPaint locationTitlePaint; + private static TextPaint locationAddressPaint; private GifDrawable gifDrawable = null; private RadialProgress radialProgress; @@ -73,6 +74,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD private ImageReceiver photoImage; private boolean photoNotSet = false; private boolean cancelLoading = false; + private int additionHeight; private boolean allowedToSetPhoto = true; @@ -97,30 +99,14 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD private ChatMediaCellDelegate mediaDelegate = null; private RectF deleteProgressRect = new RectF(); + private int captionX; + private int captionY; + private int captionHeight; + public ChatMediaCell(Context context) { super(context); - if (placeholderDocInDrawable == null) { - placeholderDocInDrawable = getResources().getDrawable(R.drawable.doc_blue); - placeholderDocOutDrawable = getResources().getDrawable(R.drawable.doc_green); - buttonStatesDrawables[0] = getResources().getDrawable(R.drawable.photoload); - buttonStatesDrawables[1] = getResources().getDrawable(R.drawable.photocancel); - buttonStatesDrawables[2] = getResources().getDrawable(R.drawable.photogif); - buttonStatesDrawables[3] = getResources().getDrawable(R.drawable.playvideo); - buttonStatesDrawables[4] = getResources().getDrawable(R.drawable.photopause); - buttonStatesDrawables[5] = getResources().getDrawable(R.drawable.burn); - buttonStatesDrawables[6] = getResources().getDrawable(R.drawable.circle); - buttonStatesDrawables[7] = getResources().getDrawable(R.drawable.photocheck); - buttonStatesDrawablesDoc[0][0] = getResources().getDrawable(R.drawable.docload_b); - buttonStatesDrawablesDoc[1][0] = getResources().getDrawable(R.drawable.doccancel_b); - buttonStatesDrawablesDoc[2][0] = getResources().getDrawable(R.drawable.docpause_b); - buttonStatesDrawablesDoc[0][1] = getResources().getDrawable(R.drawable.docload_g); - buttonStatesDrawablesDoc[1][1] = getResources().getDrawable(R.drawable.doccancel_g); - buttonStatesDrawablesDoc[2][1] = getResources().getDrawable(R.drawable.docpause_g); - videoIconDrawable = getResources().getDrawable(R.drawable.ic_video); - docMenuInDrawable = getResources().getDrawable(R.drawable.doc_actions_b); - docMenuOutDrawable = getResources().getDrawable(R.drawable.doc_actions_g); - + if (infoPaint == null) { infoPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); infoPaint.setTextSize(AndroidUtilities.dp(12)); @@ -132,6 +118,13 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD deleteProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG); deleteProgressPaint.setColor(0xffe4e2e0); + + locationTitlePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + locationTitlePaint.setTextSize(AndroidUtilities.dp(14)); + locationTitlePaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + + locationAddressPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + locationAddressPaint.setTextSize(AndroidUtilities.dp(14)); } TAG = MediaController.getInstance().generateObserverTag(); @@ -156,12 +149,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - if (photoImage != null) { - photoImage.clearImage(); - currentPhotoObject = null; - currentPhotoObjectThumb = null; - } - currentUrl = null; + photoImage.onDetachedFromWindow(); if (gifDrawable != null) { MediaController.getInstance().clearGifDrawable(this); gifDrawable = null; @@ -169,6 +157,14 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD MediaController.getInstance().removeLoadingFileObserver(this); } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (photoImage.onAttachedToWindow()) { + updateButtonState(false); + } + } + @Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); @@ -176,6 +172,78 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD boolean result = false; int side = AndroidUtilities.dp(48); + if (currentMessageObject.caption instanceof Spannable && !isPressed) { + if (event.getAction() == MotionEvent.ACTION_DOWN || (linkPreviewPressed || pressedLink != null) && event.getAction() == MotionEvent.ACTION_UP) { + if (nameLayout != null && x >= captionX && x <= captionX + backgroundWidth && y >= captionY && y <= captionY + captionHeight) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + resetPressedLink(); + try { + int x2 = (int) (x - captionX); + int y2 = (int) (y - captionY); + final int line = nameLayout.getLineForVertical(y2); + final int off = nameLayout.getOffsetForHorizontal(line, x2); + + final float left = nameLayout.getLineLeft(line); + if (left <= x2 && left + nameLayout.getLineWidth(line) >= x2) { + Spannable buffer = (Spannable) currentMessageObject.caption; + ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class); + if (link.length != 0) { + resetPressedLink(); + pressedLink = link[0]; + linkPreviewPressed = true; + result = true; + try { + int start = buffer.getSpanStart(pressedLink); + urlPath.setCurrentLayout(nameLayout, start); + nameLayout.getSelectionPath(start, buffer.getSpanEnd(pressedLink), urlPath); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } else { + resetPressedLink(); + } + } else { + resetPressedLink(); + } + } catch (Exception e) { + resetPressedLink(); + FileLog.e("tmessages", e); + } + } else if (linkPreviewPressed) { + try { + if (pressedLink instanceof URLSpanNoUnderline) { + String url = ((URLSpanNoUnderline) pressedLink).getURL(); + if (url.startsWith("@") || url.startsWith("#")) { + if (delegate != null) { + delegate.didPressUrl(url); + } + } + } else { + pressedLink.onClick(this); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + resetPressedLink(); + result = true; + } + } else { + resetPressedLink(); + } + } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { + resetPressedLink(); + } + + if (result && event.getAction() == MotionEvent.ACTION_DOWN) { + startCheckLongPress(); + } + if (event.getAction() != MotionEvent.ACTION_DOWN && event.getAction() != MotionEvent.ACTION_MOVE) { + cancelCheckLongPress(); + } + if (result) { + return result; + } + } if (event.getAction() == MotionEvent.ACTION_DOWN) { if (delegate == null || delegate.canPerformActions()) { if (buttonState != -1 && x >= buttonX && x <= buttonX + side && y >= buttonY && y <= buttonY + side) { @@ -314,20 +382,20 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD Drawable currentButtonDrawable = null; if (currentMessageObject.type == 9 && gifDrawable == null) { if (buttonState == 1 && !currentMessageObject.isSending()) { - return buttonStatesDrawablesDoc[2][currentMessageObject.isOut() ? 1 : 0]; + return ResourceLoader.buttonStatesDrawablesDoc[2][currentMessageObject.isOut() ? 1 : 0]; } else { - return buttonStatesDrawablesDoc[buttonState][currentMessageObject.isOut() ? 1 : 0]; + return ResourceLoader.buttonStatesDrawablesDoc[buttonState][currentMessageObject.isOut() ? 1 : 0]; } } else { if (buttonState == 1 && !currentMessageObject.isSending()) { - return buttonStatesDrawables[4]; + return ResourceLoader.buttonStatesDrawables[4]; } else { - return buttonStatesDrawables[buttonState]; + return ResourceLoader.buttonStatesDrawables[buttonState]; } } } else if (buttonState == -1) { if (currentMessageObject.type == 9 && gifDrawable == null) { - return currentMessageObject.isOut() ? placeholderDocOutDrawable : placeholderDocInDrawable; + return currentMessageObject.isOut() ? ResourceLoader.placeholderDocOutDrawable : ResourceLoader.placeholderDocInDrawable; } } return null; @@ -338,7 +406,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD cancelLoading = false; radialProgress.setProgress(0, false); if (currentMessageObject.type == 1) { - photoImage.setImage(currentPhotoObject.location, currentPhotoFilter, currentPhotoObjectThumb != null ? currentPhotoObjectThumb.location : null, currentPhotoFilter, currentPhotoObject.size, false); + photoImage.setImage(currentPhotoObject.location, currentPhotoFilter, currentPhotoObjectThumb != null ? currentPhotoObjectThumb.location : null, currentPhotoFilter, currentPhotoObject.size, null, false); } else if (currentMessageObject.type == 8 || currentMessageObject.type == 9) { FileLoader.getInstance().loadFile(currentMessageObject.messageOwner.media.document, true, false); lastDownloadedGifMessage = currentMessageObject; @@ -393,7 +461,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD } double lat = object.messageOwner.media.geo.lat; double lon = object.messageOwner.media.geo._long; - String url = String.format(Locale.US, "https://maps.googleapis.com/maps/api/staticmap?center=%f,%f&zoom=13&size=100x100&maptype=roadmap&scale=%d&markers=color:red|size:big|%f,%f&sensor=false", lat, lon, Math.min(2, (int)Math.ceil(AndroidUtilities.density)), lat, lon); + String url = String.format(Locale.US, "https://maps.googleapis.com/maps/api/staticmap?center=%f,%f&zoom=13&size=100x100&maptype=roadmap&scale=%d&markers=color:red|size:big|%f,%f&sensor=false", lat, lon, Math.min(2, (int) Math.ceil(AndroidUtilities.density)), lat, lon); if (!url.equals(currentUrl)) { return true; } @@ -410,10 +478,12 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD @Override public void setMessageObject(MessageObject messageObject) { - media = messageObject.type != 9; boolean dataChanged = currentMessageObject == messageObject && (isUserDataChanged() || photoNotSet); if (currentMessageObject != messageObject || isPhotoDataChanged(messageObject) || dataChanged) { + media = messageObject.type != 9; cancelLoading = false; + additionHeight = 0; + resetPressedLink(); buttonState = -1; gifDrawable = null; @@ -437,9 +507,12 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD } if (currentNameString == null || !currentNameString.equals(name)) { currentNameString = name; - nameWidth = Math.min(maxWidth, (int) Math.ceil(namePaint.measureText(currentNameString))); - CharSequence str = TextUtils.ellipsize(currentNameString, namePaint, nameWidth, TextUtils.TruncateAt.END); - nameLayout = new StaticLayout(str, namePaint, nameWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + nameLayout = StaticLayoutEx.createStaticLayout(currentNameString, namePaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false, TextUtils.TruncateAt.END, maxWidth, 1); + if (nameLayout.getLineCount() > 0) { + nameWidth = Math.min(maxWidth, (int) Math.ceil(nameLayout.getLineWidth(0))); + } else { + nameWidth = maxWidth; + } } String fileName = messageObject.getFileName(); @@ -453,7 +526,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD } ext = ext.toUpperCase(); - String str = Utilities.formatFileSize(messageObject.messageOwner.media.document.size) + " " + ext; + String str = AndroidUtilities.formatFileSize(messageObject.messageOwner.media.document.size) + " " + ext; if (currentInfoString == null || !currentInfoString.equals(str)) { currentInfoString = str; @@ -465,7 +538,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD } else if (messageObject.type == 8) { gifDrawable = MediaController.getInstance().getGifDrawable(this, false); - String str = Utilities.formatFileSize(messageObject.messageOwner.media.document.size); + String str = AndroidUtilities.formatFileSize(messageObject.messageOwner.media.document.size); if (currentInfoString == null || !currentInfoString.equals(str)) { currentInfoString = str; infoOffset = 0; @@ -478,10 +551,10 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD int duration = messageObject.messageOwner.media.video.duration; int minutes = duration / 60; int seconds = duration - minutes * 60; - String str = String.format("%d:%02d, %s", minutes, seconds, Utilities.formatFileSize(messageObject.messageOwner.media.video.size)); + String str = String.format("%d:%02d, %s", minutes, seconds, AndroidUtilities.formatFileSize(messageObject.messageOwner.media.video.size)); if (currentInfoString == null || !currentInfoString.equals(str)) { currentInfoString = str; - infoOffset = videoIconDrawable.getIntrinsicWidth() + AndroidUtilities.dp(4); + infoOffset = ResourceLoader.videoIconDrawable.getIntrinsicWidth() + AndroidUtilities.dp(4); infoWidth = (int) Math.ceil(infoPaint.measureText(currentInfoString)); infoLayout = new StaticLayout(currentInfoString, infoPaint, infoWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); } @@ -504,22 +577,50 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD photoImage.setParentMessageObject(messageObject); if (currentPhotoObject != null) { currentPhotoFilter = String.format(Locale.US, "%d_%d_b", photoWidth, photoHeight); - photoImage.setImage(null, null, null, null, currentPhotoObject.location, currentPhotoFilter, 0, true); + photoImage.setImage(null, null, null, null, currentPhotoObject.location, currentPhotoFilter, 0, null, true); } else { photoImage.setImageBitmap((BitmapDrawable) null); } } else if (messageObject.type == 4) { //geo - photoWidth = AndroidUtilities.dp(200); - photoHeight = AndroidUtilities.dp(100); - backgroundWidth = photoWidth + AndroidUtilities.dp(12); - double lat = messageObject.messageOwner.media.geo.lat; double lon = messageObject.messageOwner.media.geo._long; - currentUrl = String.format(Locale.US, "https://maps.googleapis.com/maps/api/staticmap?center=%f,%f&zoom=13&size=200x100&maptype=roadmap&scale=%d&markers=color:red|size:big|%f,%f&sensor=false", lat, lon, Math.min(2, (int)Math.ceil(AndroidUtilities.density)), lat, lon); + + if (messageObject.messageOwner.media.title != null && messageObject.messageOwner.media.title.length() > 0) { + int maxWidth = (AndroidUtilities.isTablet() ? AndroidUtilities.getMinTabletSide() : Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y)) - AndroidUtilities.dp((isChat && !messageObject.isOut() ? 102 : 40) + 86 + 24); + nameLayout = StaticLayoutEx.createStaticLayout(messageObject.messageOwner.media.title, locationTitlePaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false, TextUtils.TruncateAt.END, maxWidth - AndroidUtilities.dp(4), 3); + int lineCount = nameLayout.getLineCount(); + if (messageObject.messageOwner.media.address != null && messageObject.messageOwner.media.address.length() > 0) { + infoLayout = StaticLayoutEx.createStaticLayout(messageObject.messageOwner.media.address, locationAddressPaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false, TextUtils.TruncateAt.END, maxWidth - AndroidUtilities.dp(4), Math.min(3, 4 - lineCount)); + } else { + infoLayout = null; + } + + media = false; + measureTime(messageObject); + photoWidth = AndroidUtilities.dp(86); + photoHeight = AndroidUtilities.dp(86); + maxWidth = timeWidth + AndroidUtilities.dp(messageObject.isOut() ? 29 : 9); + for (int a = 0; a < lineCount; a++) { + maxWidth = (int) Math.max(maxWidth, nameLayout.getLineWidth(a) + AndroidUtilities.dp(16)); + } + if (infoLayout != null) { + for (int a = 0; a < infoLayout.getLineCount(); a++) { + maxWidth = (int) Math.max(maxWidth, infoLayout.getLineWidth(a) + AndroidUtilities.dp(16)); + } + } + backgroundWidth = photoWidth + AndroidUtilities.dp(21) + maxWidth; + currentUrl = String.format(Locale.US, "https://maps.googleapis.com/maps/api/staticmap?center=%f,%f&zoom=13&size=72x72&maptype=roadmap&scale=%d&markers=color:red|size:big|%f,%f&sensor=false", lat, lon, Math.min(2, (int) Math.ceil(AndroidUtilities.density)), lat, lon); + } else { + photoWidth = AndroidUtilities.dp(200); + photoHeight = AndroidUtilities.dp(100); + backgroundWidth = photoWidth + AndroidUtilities.dp(12); + currentUrl = String.format(Locale.US, "https://maps.googleapis.com/maps/api/staticmap?center=%f,%f&zoom=13&size=200x100&maptype=roadmap&scale=%d&markers=color:red|size:big|%f,%f&sensor=false", lat, lon, Math.min(2, (int) Math.ceil(AndroidUtilities.density)), lat, lon); + } + photoImage.setNeedsQualityThumb(false); photoImage.setShouldGenerateQualityThumb(false); photoImage.setParentMessageObject(null); - photoImage.setImage(currentUrl, null, null, 0); + photoImage.setImage(currentUrl, null, messageObject.isOut() ? ResourceLoader.geoOutDrawable : ResourceLoader.geoInDrawable, null, 0); } else if (messageObject.type == 13) { //webp drawBackground = false; for (TLRPC.DocumentAttribute attribute : messageObject.messageOwner.media.document.attributes) { @@ -542,11 +643,11 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD } if (photoHeight > maxHeight) { photoWidth *= maxHeight / photoHeight; - photoHeight = (int)maxHeight; + photoHeight = (int) maxHeight; } if (photoWidth > maxWidth) { photoHeight *= maxWidth / photoWidth; - photoWidth = (int)maxWidth; + photoWidth = (int) maxWidth; } backgroundWidth = photoWidth + AndroidUtilities.dp(12); currentPhotoObjectThumb = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, 80); @@ -561,7 +662,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD null, currentPhotoObjectThumb != null ? currentPhotoObjectThumb.location : null, "b1", - messageObject.messageOwner.media.document.size, true); + messageObject.messageOwner.media.document.size, "webp", true); } } else if (messageObject.messageOwner.media.document.id != 0) { photoImage.setImage(messageObject.messageOwner.media.document, null, @@ -569,7 +670,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD null, currentPhotoObjectThumb != null ? currentPhotoObjectThumb.location : null, "b1", - messageObject.messageOwner.media.document.size, true); + messageObject.messageOwner.media.document.size, "webp", true); } } else { if (AndroidUtilities.isTablet()) { @@ -602,10 +703,16 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD } //8 - gif, 1 - photo, 3 - video + if (messageObject.caption != null) { + media = false; + } currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, AndroidUtilities.getPhotoSize()); if (currentPhotoObject != null) { + if (currentPhotoObject == currentPhotoObjectThumb) { + currentPhotoObjectThumb = null; + } boolean noSize = false; if (messageObject.type == 3 || messageObject.type == 8) { noSize = true; @@ -640,6 +747,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD w = (int) (currentPhotoObject.w / hScale); } } + measureTime(messageObject); int timeWidthTotal = timeWidth + AndroidUtilities.dp(14 + (messageObject.isOut() ? 20 : 0)); if (w < timeWidthTotal) { w = timeWidthTotal; @@ -656,6 +764,20 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD photoWidth = w; photoHeight = h; backgroundWidth = w + AndroidUtilities.dp(12); + if (!media) { + backgroundWidth += AndroidUtilities.dp(9); + } + if (messageObject.caption != null) { + nameLayout = new StaticLayout(messageObject.caption, MessageObject.textPaint, photoWidth - AndroidUtilities.dp(10), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + if (nameLayout.getLineCount() > 0) { + captionHeight = nameLayout.getHeight(); + additionHeight += captionHeight + AndroidUtilities.dp(9); + float lastLineWidth = nameLayout.getLineWidth(nameLayout.getLineCount() - 1) + nameLayout.getLineLeft(nameLayout.getLineCount() - 1); + if (photoWidth - AndroidUtilities.dp(8) - lastLineWidth < timeWidthTotal) { + additionHeight += AndroidUtilities.dp(14); + } + } + } currentPhotoFilter = String.format(Locale.US, "%d_%d", (int) (w / AndroidUtilities.density), (int) (h / AndroidUtilities.density)); if (messageObject.photoThumbs.size() > 1 || messageObject.type == 3 || messageObject.type == 8) { @@ -679,25 +801,25 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD if (photoExist || MediaController.getInstance().canDownloadMedia(MediaController.AUTODOWNLOAD_MASK_PHOTO) || FileLoader.getInstance().isLoadingFile(fileName)) { if (allowedToSetPhoto || ImageLoader.getInstance().getImageFromMemory(currentPhotoObject.location, null, currentPhotoFilter) != null) { allowedToSetPhoto = true; - photoImage.setImage(currentPhotoObject.location, currentPhotoFilter, currentPhotoObjectThumb != null ? currentPhotoObjectThumb.location : null, currentPhotoFilter, noSize ? 0 : currentPhotoObject.size, false); + photoImage.setImage(currentPhotoObject.location, currentPhotoFilter, currentPhotoObjectThumb != null ? currentPhotoObjectThumb.location : null, currentPhotoFilter, noSize ? 0 : currentPhotoObject.size, null, false); } else if (currentPhotoObjectThumb != null) { - photoImage.setImage(null, null, currentPhotoObjectThumb.location, currentPhotoFilter, 0, false); + photoImage.setImage(null, null, currentPhotoObjectThumb.location, currentPhotoFilter, 0, null, false); } else { photoImage.setImageBitmap((Drawable) null); } } else { photoNotSet = true; if (currentPhotoObjectThumb != null) { - photoImage.setImage(null, null, currentPhotoObjectThumb.location, currentPhotoFilter, 0, false); + photoImage.setImage(null, null, currentPhotoObjectThumb.location, currentPhotoFilter, 0, null, false); } else { photoImage.setImageBitmap((Drawable) null); } } } else { - photoImage.setImage(null, null, currentPhotoObject.location, currentPhotoFilter, 0, false); + photoImage.setImage(null, null, currentPhotoObject.location, currentPhotoFilter, 0, null, false); } } else { - photoImage.setImageBitmap((Bitmap)null); + photoImage.setImageBitmap((Bitmap) null); } } super.setMessageObject(messageObject); @@ -793,7 +915,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), photoHeight + AndroidUtilities.dp(14) + namesOffset); + setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), photoHeight + AndroidUtilities.dp(14) + namesOffset + additionHeight); } @Override @@ -816,8 +938,8 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD } photoImage.setImageCoords(x, AndroidUtilities.dp(7) + namesOffset, photoWidth, photoHeight); int size = AndroidUtilities.dp(48); - buttonX = (int)(x + (photoWidth - size) / 2.0f); - buttonY = (int)(AndroidUtilities.dp(7) + (photoHeight - size) / 2.0f) + namesOffset; + buttonX = (int) (x + (photoWidth - size) / 2.0f); + buttonY = (int) (AndroidUtilities.dp(7) + (photoHeight - size) / 2.0f) + namesOffset; radialProgress.setProgressRect(buttonX, buttonY, buttonX + AndroidUtilities.dp(48), buttonY + AndroidUtilities.dp(48)); deleteProgressRect.set(buttonX + AndroidUtilities.dp(3), buttonY + AndroidUtilities.dp(3), buttonX + AndroidUtilities.dp(45), buttonY + AndroidUtilities.dp(45)); @@ -834,7 +956,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD if (currentInfoString == null || !currentInfoString.equals(str)) { currentInfoString = str; infoOffset = 0; - infoWidth = (int)Math.ceil(infoPaint.measureText(currentInfoString)); + infoWidth = (int) Math.ceil(infoPaint.measureText(currentInfoString)); CharSequence str2 = TextUtils.ellipsize(currentInfoString, infoPaint, infoWidth, TextUtils.TruncateAt.END); infoLayout = new StaticLayout(str2, infoPaint, infoWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); invalidate(); @@ -874,15 +996,15 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD radialProgress.setHideCurrentDrawable(false); if (currentMessageObject.type == 9) { - Drawable menuDrawable = null; + Drawable menuDrawable; if (currentMessageObject.isOut()) { infoPaint.setColor(0xff70b15c); docBackPaint.setColor(0xffdaf5c3); - menuDrawable = docMenuOutDrawable; + menuDrawable = ResourceLoader.docMenuOutDrawable; } else { infoPaint.setColor(0xffa1adbb); docBackPaint.setColor(0xffebf0f5); - menuDrawable = docMenuInDrawable; + menuDrawable = ResourceLoader.docMenuInDrawable; } setDrawableBounds(menuDrawable, photoImage.getImageX() + backgroundWidth - AndroidUtilities.dp(44), AndroidUtilities.dp(10) + namesOffset); @@ -891,15 +1013,15 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD if (buttonState >= 0 && buttonState < 4) { if (!imageDrawn) { if (buttonState == 1 && !currentMessageObject.isSending()) { - radialProgress.swapBackground(buttonStatesDrawablesDoc[2][currentMessageObject.isOut() ? 1 : 0]); + radialProgress.swapBackground(ResourceLoader.buttonStatesDrawablesDoc[2][currentMessageObject.isOut() ? 1 : 0]); } else { - radialProgress.swapBackground(buttonStatesDrawablesDoc[buttonState][currentMessageObject.isOut() ? 1 : 0]); + radialProgress.swapBackground(ResourceLoader.buttonStatesDrawablesDoc[buttonState][currentMessageObject.isOut() ? 1 : 0]); } } else { if (buttonState == 1 && !currentMessageObject.isSending()) { - radialProgress.swapBackground(buttonStatesDrawables[4]); + radialProgress.swapBackground(ResourceLoader.buttonStatesDrawables[4]); } else { - radialProgress.swapBackground(buttonStatesDrawables[buttonState]); + radialProgress.swapBackground(ResourceLoader.buttonStatesDrawables[buttonState]); } } } @@ -930,16 +1052,16 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD drawable = 6; } } - setDrawableBounds(buttonStatesDrawables[drawable], buttonX, buttonY); - buttonStatesDrawables[drawable].setAlpha((int)(255 * (1.0f - radialProgress.getAlpha()))); - buttonStatesDrawables[drawable].draw(canvas); + setDrawableBounds(ResourceLoader.buttonStatesDrawables[drawable], buttonX, buttonY); + ResourceLoader.buttonStatesDrawables[drawable].setAlpha((int) (255 * (1.0f - radialProgress.getAlpha()))); + ResourceLoader.buttonStatesDrawables[drawable].draw(canvas); if (!currentMessageObject.isOut() && currentMessageObject.messageOwner.destroyTime != 0) { long msTime = System.currentTimeMillis() + ConnectionsManager.getInstance().getTimeDifference() * 1000; - float progress = Math.max(0, (long)currentMessageObject.messageOwner.destroyTime * 1000 - msTime) / (currentMessageObject.messageOwner.ttl * 1000.0f); + float progress = Math.max(0, (long) currentMessageObject.messageOwner.destroyTime * 1000 - msTime) / (currentMessageObject.messageOwner.ttl * 1000.0f); canvas.drawArc(deleteProgressRect, -90, -360 * progress, true, deleteProgressPaint); if (progress != 0) { int offset = AndroidUtilities.dp(2); - invalidate((int)deleteProgressRect.left - offset, (int)deleteProgressRect.top - offset, (int)deleteProgressRect.right + offset * 2, (int)deleteProgressRect.bottom + offset * 2); + invalidate((int) deleteProgressRect.left - offset, (int) deleteProgressRect.top - offset, (int) deleteProgressRect.right + offset * 2, (int) deleteProgressRect.bottom + offset * 2); } updateSecretTimeText(); } @@ -947,7 +1069,48 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD radialProgress.onDraw(canvas); - if (nameLayout != null) { + if (currentMessageObject.type == 1 || currentMessageObject.type == 3) { + if (nameLayout != null) { + canvas.save(); + canvas.translate(captionX = photoImage.getImageX() + AndroidUtilities.dp(5), captionY = photoImage.getImageY() + photoHeight + AndroidUtilities.dp(6)); + if (pressedLink != null) { + canvas.drawPath(urlPath, urlPaint); + } + nameLayout.draw(canvas); + canvas.restore(); + } + if (infoLayout != null && (buttonState == 1 || buttonState == 0 || buttonState == 3 || currentMessageObject.isSecretPhoto())) { + infoPaint.setColor(0xffffffff); + setDrawableBounds(ResourceLoader.mediaBackgroundDrawable, photoImage.getImageX() + AndroidUtilities.dp(4), photoImage.getImageY() + AndroidUtilities.dp(4), infoWidth + AndroidUtilities.dp(8) + infoOffset, AndroidUtilities.dp(16.5f)); + ResourceLoader.mediaBackgroundDrawable.draw(canvas); + + if (currentMessageObject.type == 3) { + setDrawableBounds(ResourceLoader.videoIconDrawable, photoImage.getImageX() + AndroidUtilities.dp(8), photoImage.getImageY() + AndroidUtilities.dp(7.5f)); + ResourceLoader.videoIconDrawable.draw(canvas); + } + + canvas.save(); + canvas.translate(photoImage.getImageX() + AndroidUtilities.dp(8) + infoOffset, photoImage.getImageY() + AndroidUtilities.dp(5.5f)); + infoLayout.draw(canvas); + canvas.restore(); + } + } else if (currentMessageObject.type == 4) { + if (nameLayout != null) { + locationAddressPaint.setColor(currentMessageObject.isOut() ? 0xff70b15c : 0xff999999); + + canvas.save(); + canvas.translate(photoImage.getImageX() + photoImage.getImageWidth() + AndroidUtilities.dp(10), photoImage.getImageY() + AndroidUtilities.dp(3)); + nameLayout.draw(canvas); + canvas.restore(); + + if (infoLayout != null) { + canvas.save(); + canvas.translate(photoImage.getImageX() + photoImage.getImageWidth() + AndroidUtilities.dp(10), photoImage.getImageY() + AndroidUtilities.dp(nameLayout.getLineCount() * 16 + 5)); + infoLayout.draw(canvas); + canvas.restore(); + } + } + } else if (nameLayout != null) { canvas.save(); canvas.translate(photoImage.getImageX() + photoImage.getImageWidth() + AndroidUtilities.dp(10), photoImage.getImageY() + AndroidUtilities.dp(8)); nameLayout.draw(canvas); @@ -959,20 +1122,6 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD infoLayout.draw(canvas); canvas.restore(); } - } else if (infoLayout != null && (buttonState == 1 || buttonState == 0 || buttonState == 3 || currentMessageObject.isSecretPhoto())) { - infoPaint.setColor(0xffffffff); - setDrawableBounds(mediaBackgroundDrawable, photoImage.getImageX() + AndroidUtilities.dp(4), photoImage.getImageY() + AndroidUtilities.dp(4), infoWidth + AndroidUtilities.dp(8) + infoOffset, AndroidUtilities.dp(16.5f)); - mediaBackgroundDrawable.draw(canvas); - - if (currentMessageObject.type == 3) { - setDrawableBounds(videoIconDrawable, photoImage.getImageX() + AndroidUtilities.dp(8), photoImage.getImageY() + AndroidUtilities.dp(7.5f)); - videoIconDrawable.draw(canvas); - } - - canvas.save(); - canvas.translate(photoImage.getImageX() + AndroidUtilities.dp(8) + infoOffset, photoImage.getImageY() + AndroidUtilities.dp(5.5f)); - infoLayout.draw(canvas); - canvas.restore(); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java index 9098aaf1d..8e5924c37 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java @@ -12,9 +12,9 @@ import android.content.Context; import android.content.Intent; import android.graphics.Canvas; import android.graphics.Paint; -import android.graphics.Path; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Build; import android.provider.Browser; import android.text.Layout; import android.text.Spannable; @@ -33,20 +33,18 @@ import org.telegram.messenger.FileLog; import org.telegram.android.MessageObject; import org.telegram.messenger.R; import org.telegram.messenger.TLRPC; +import org.telegram.ui.Components.ResourceLoader; import org.telegram.ui.Components.StaticLayoutEx; import org.telegram.ui.Components.URLSpanNoUnderline; +import java.io.File; import java.util.Locale; public class ChatMessageCell extends ChatBaseCell { private int textX, textY; private int totalHeight = 0; - private ClickableSpan pressedLink; private int linkBlockNum; - private MyPath urlPath = new MyPath(); - private boolean linkPreviewPressed; - private static Paint urlPaint; private int lastVisibleBlockNum = 0; private int firstVisibleBlockNum = 0; @@ -58,72 +56,24 @@ public class ChatMessageCell extends ChatBaseCell { private boolean hasLinkPreview; private int linkPreviewHeight; private boolean isInstagram; - private int smallImageX; private int descriptionY; private int durationWidth; + private int descriptionX; + private int titleX; + private int authorX; private StaticLayout siteNameLayout; private StaticLayout titleLayout; private StaticLayout descriptionLayout; private StaticLayout durationLayout; private StaticLayout authorLayout; private static TextPaint durationPaint; - private TLRPC.PhotoSize currentPhotoObject; - private TLRPC.PhotoSize currentPhotoObjectThumb; - private boolean imageCleared; private static Drawable igvideoDrawable; - private class MyPath extends Path { - - private StaticLayout currentLayout; - private int currentLine; - private float lastTop = -1; - - public void setCurrentLayout(StaticLayout layout, int start) { - currentLayout = layout; - currentLine = layout.getLineForOffset(start); - lastTop = -1; - } - - @Override - public void addRect(float left, float top, float right, float bottom, Direction dir) { - if (lastTop == -1) { - lastTop = top; - } else if (lastTop != top) { - lastTop = top; - currentLine++; - } - float lineRight = currentLayout.getLineRight(currentLine); - float lineLeft = currentLayout.getLineLeft(currentLine); - if (left >= lineRight) { - return; - } - if (right > lineRight) { - right = lineRight; - } - if (left < lineLeft) { - left = lineLeft; - } - super.addRect(left, top, right, bottom, dir); - } - } - public ChatMessageCell(Context context) { super(context); drawForwardedName = true; linkImageView = new ImageReceiver(this); - if (urlPaint == null) { - urlPaint = new Paint(); - urlPaint.setColor(0x33316f9f); - } - } - - private void resetPressedLink() { - if (pressedLink != null) { - pressedLink = null; - } - linkPreviewPressed = false; - invalidate(); } @Override @@ -131,22 +81,22 @@ public class ChatMessageCell extends ChatBaseCell { boolean result = false; if (currentMessageObject != null && currentMessageObject.textLayoutBlocks != null && !currentMessageObject.textLayoutBlocks.isEmpty() && currentMessageObject.messageText instanceof Spannable && !isPressed) { if (event.getAction() == MotionEvent.ACTION_DOWN || (linkPreviewPressed || pressedLink != null) && event.getAction() == MotionEvent.ACTION_UP) { - int x = (int)event.getX(); - int y = (int)event.getY(); + int x = (int) event.getX(); + int y = (int) event.getY(); if (x >= textX && y >= textY && x <= textX + currentMessageObject.textWidth && y <= textY + currentMessageObject.textHeight) { y -= textY; int blockNum = Math.max(0, y / currentMessageObject.blockHeight); if (blockNum < currentMessageObject.textLayoutBlocks.size()) { try { MessageObject.TextLayoutBlock block = currentMessageObject.textLayoutBlocks.get(blockNum); - x -= textX - (int)Math.ceil(block.textXOffset); + x -= textX - (int) Math.ceil(block.textXOffset); y -= block.textYOffset; final int line = block.textLayout.getLineForVertical(y); final int off = block.textLayout.getOffsetForHorizontal(line, x) + block.charactersOffset; final float left = block.textLayout.getLineLeft(line); if (left <= x && left + block.textLayout.getLineWidth(line) >= x) { - Spannable buffer = (Spannable)currentMessageObject.messageText; + Spannable buffer = (Spannable) currentMessageObject.messageText; ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class); if (link.length != 0) { if (event.getAction() == MotionEvent.ACTION_DOWN) { @@ -203,7 +153,7 @@ public class ChatMessageCell extends ChatBaseCell { } else { if (descriptionLayout != null && y >= descriptionY) { try { - x -= textX + AndroidUtilities.dp(10); + x -= textX + AndroidUtilities.dp(10) + descriptionX; y -= descriptionY; final int line = descriptionLayout.getLineForVertical(y); final int off = descriptionLayout.getOffsetForHorizontal(line, x); @@ -242,10 +192,15 @@ public class ChatMessageCell extends ChatBaseCell { if (pressedLink != null) { pressedLink.onClick(this); } else { - Uri uri = Uri.parse(currentMessageObject.messageOwner.media.webpage.url); - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - intent.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName()); - getContext().startActivity(intent); + TLRPC.WebPage webPage = currentMessageObject.messageOwner.media.webpage; + if (Build.VERSION.SDK_INT >= 16 && webPage.embed_url != null && webPage.embed_url.length() != 0) { + delegate.needOpenWebView(webPage.embed_url, webPage.site_name, webPage.url, webPage.embed_width, webPage.embed_height); + } else { + Uri uri = Uri.parse(webPage.url); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + intent.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName()); + getContext().startActivity(intent); + } } } catch (Exception e) { FileLog.e("tmessages", e); @@ -311,6 +266,9 @@ public class ChatMessageCell extends ChatBaseCell { int addedChars = 0; StaticLayout layout = new StaticLayout(text, paint, smallWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); for (int a = 0; a < linesCount; a++) { + if (layout.getLineLeft(a) != 0) { + maxWidth = smallWidth; + } int pos = layout.getLineEnd(a); if (pos == text.length()) { break; @@ -318,7 +276,7 @@ public class ChatMessageCell extends ChatBaseCell { pos--; if (stringBuilder.charAt(pos + addedChars) == ' ') { stringBuilder.replace(pos + addedChars, pos + addedChars + 1, "\n"); - } else { + } else if (stringBuilder.charAt(pos + addedChars) != '\n') { stringBuilder.insert(pos + addedChars, "\n"); addedChars++; } @@ -331,7 +289,7 @@ public class ChatMessageCell extends ChatBaseCell { @Override protected boolean isUserDataChanged() { - if (imageCleared || !hasLinkPreview && currentMessageObject.messageOwner.media != null && currentMessageObject.messageOwner.media.webpage instanceof TLRPC.TL_webPage) { + if (!hasLinkPreview && currentMessageObject.messageOwner.media != null && currentMessageObject.messageOwner.media.webpage instanceof TLRPC.TL_webPage) { return true; } //suppress warning @@ -341,14 +299,13 @@ public class ChatMessageCell extends ChatBaseCell { @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - if (linkImageView != null) { - linkImageView.clearImage(); - if (currentPhotoObject != null) { - imageCleared = true; - currentPhotoObject = null; - currentPhotoObjectThumb = null; - } - } + linkImageView.onDetachedFromWindow(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + linkImageView.onAttachedToWindow(); } @Override @@ -363,16 +320,12 @@ public class ChatMessageCell extends ChatBaseCell { resetPressedLink(); linkPreviewPressed = false; linkPreviewHeight = 0; - smallImageX = 0; isInstagram = false; durationLayout = null; descriptionLayout = null; titleLayout = null; siteNameLayout = null; authorLayout = null; - currentPhotoObject = null; - imageCleared = false; - currentPhotoObjectThumb = null; int maxWidth; if (AndroidUtilities.isTablet()) { @@ -404,6 +357,7 @@ public class ChatMessageCell extends ChatBaseCell { maxChildWidth = Math.max(maxChildWidth, forwardedNameWidth); maxChildWidth = Math.max(maxChildWidth, replyNameWidth); maxChildWidth = Math.max(maxChildWidth, replyTextWidth); + int maxWebWidth = 0; int timeMore = timeWidth + AndroidUtilities.dp(6); if (messageObject.isOut()) { @@ -425,21 +379,25 @@ public class ChatMessageCell extends ChatBaseCell { linkPreviewMaxWidth = Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) - AndroidUtilities.dp(80); } } + + TLRPC.TL_webPage webPage = (TLRPC.TL_webPage) messageObject.messageOwner.media.webpage; + + if (webPage.site_name != null && webPage.photo != null && webPage.site_name.toLowerCase().equals("instagram")) { + linkPreviewMaxWidth = Math.max(AndroidUtilities.displaySize.y / 3, currentMessageObject.textWidth); + } + int additinalWidth = AndroidUtilities.dp(10); int restLinesCount = 3; int additionalHeight = 0; linkPreviewMaxWidth -= additinalWidth; hasLinkPreview = true; - TLRPC.TL_webPage webPage = (TLRPC.TL_webPage) messageObject.messageOwner.media.webpage; if (currentMessageObject.photoThumbs == null && webPage.photo != null) { currentMessageObject.generateThumbs(true); } - if (MediaController.getInstance().canDownloadMedia(MediaController.AUTODOWNLOAD_MASK_PHOTO)) { - isSmallImage = webPage.description != null && webPage.type != null && (webPage.type.equals("app") || webPage.type.equals("profile") || webPage.type.equals("article")) && currentMessageObject.photoThumbs != null; - } + isSmallImage = webPage.description != null && webPage.type != null && (webPage.type.equals("app") || webPage.type.equals("profile") || webPage.type.equals("article")) && currentMessageObject.photoThumbs != null; if (webPage.site_name != null) { try { @@ -449,36 +407,54 @@ public class ChatMessageCell extends ChatBaseCell { linkPreviewHeight += height; totalHeight += height; additionalHeight += height; + width = siteNameLayout.getWidth(); maxChildWidth = Math.max(maxChildWidth, width + additinalWidth); + maxWebWidth = Math.max(maxWebWidth, width + additinalWidth); } catch (Exception e) { FileLog.e("tmessages", e); } } + boolean titleIsRTL = false; if (webPage.title != null) { try { + titleX = 0; if (linkPreviewHeight != 0) { linkPreviewHeight += AndroidUtilities.dp(2); totalHeight += AndroidUtilities.dp(2); } int restLines = 0; if (!isSmallImage || webPage.description == null) { - titleLayout = StaticLayoutEx.createStaticLayout(webPage.title, replyNamePaint, linkPreviewMaxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, AndroidUtilities.dp(1), false, TextUtils.TruncateAt.END, linkPreviewMaxWidth, 2); + titleLayout = StaticLayoutEx.createStaticLayout(webPage.title, replyNamePaint, linkPreviewMaxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, AndroidUtilities.dp(1), false, TextUtils.TruncateAt.END, linkPreviewMaxWidth, 4); } else { restLines = restLinesCount; - titleLayout = generateStaticLayout(webPage.title, replyNamePaint, linkPreviewMaxWidth, linkPreviewMaxWidth - AndroidUtilities.dp(48 + 2), restLinesCount, 2); + titleLayout = generateStaticLayout(webPage.title, replyNamePaint, linkPreviewMaxWidth, linkPreviewMaxWidth - AndroidUtilities.dp(48 + 2), restLinesCount, 4); restLinesCount -= titleLayout.getLineCount(); } int height = titleLayout.getLineBottom(titleLayout.getLineCount() - 1); linkPreviewHeight += height; totalHeight += height; for (int a = 0; a < titleLayout.getLineCount(); a++) { - int width = (int) Math.ceil(titleLayout.getLineWidth(a)); - if (a < restLines) { - smallImageX = Math.max(smallImageX, width); + int lineLeft = (int) titleLayout.getLineLeft(a); + if (lineLeft != 0) { + titleIsRTL = true; + if (titleX == 0) { + titleX = -lineLeft; + } else { + titleX = Math.max(titleX, -lineLeft); + } + } + int width; + if (lineLeft != 0) { + width = titleLayout.getWidth() - lineLeft; + } else { + width = (int) Math.ceil(titleLayout.getLineWidth(a)); + } + if (a < restLines || lineLeft != 0 && isSmallImage) { width += AndroidUtilities.dp(48 + 2); } maxChildWidth = Math.max(maxChildWidth, width + additinalWidth); + maxWebWidth = Math.max(maxWebWidth, width + additinalWidth); } } catch (Exception e) { FileLog.e("tmessages", e); @@ -501,7 +477,10 @@ public class ChatMessageCell extends ChatBaseCell { int height = authorLayout.getLineBottom(authorLayout.getLineCount() - 1); linkPreviewHeight += height; totalHeight += height; + int lineLeft = (int) authorLayout.getLineLeft(0); + authorX = -lineLeft; maxChildWidth = Math.max(maxChildWidth, width + additinalWidth); + maxWebWidth = Math.max(maxWebWidth, width + additinalWidth); } catch (Exception e) { FileLog.e("tmessages", e); } @@ -509,6 +488,7 @@ public class ChatMessageCell extends ChatBaseCell { if (webPage.description != null) { try { + descriptionX = 0; currentMessageObject.generateLinkDescription(); if (linkPreviewHeight != 0) { linkPreviewHeight += AndroidUtilities.dp(2); @@ -525,11 +505,28 @@ public class ChatMessageCell extends ChatBaseCell { linkPreviewHeight += height; totalHeight += height; for (int a = 0; a < descriptionLayout.getLineCount(); a++) { - int width = (int) Math.ceil(descriptionLayout.getLineWidth(a)); - if (a < restLines) { - smallImageX = Math.max(smallImageX, width); + int lineLeft = (int) Math.ceil(descriptionLayout.getLineLeft(a)); + if (descriptionX == 0) { + descriptionX = -lineLeft; + } else { + descriptionX = Math.max(descriptionX, -lineLeft); + } + + int width; + if (lineLeft != 0) { + width = descriptionLayout.getWidth() - lineLeft; + } else { + width = (int) Math.ceil(descriptionLayout.getLineWidth(a)); + } + if (a < restLines || lineLeft != 0 && isSmallImage) { width += AndroidUtilities.dp(48 + 2); } + if (maxWebWidth < width + additinalWidth) { + if (titleIsRTL) { + titleX += (width + additinalWidth - maxWebWidth); + } + maxWebWidth = width + additinalWidth; + } maxChildWidth = Math.max(maxChildWidth, width + additinalWidth); } } catch (Exception e) { @@ -537,15 +534,15 @@ public class ChatMessageCell extends ChatBaseCell { } } - if (webPage.photo != null && MediaController.getInstance().canDownloadMedia(MediaController.AUTODOWNLOAD_MASK_PHOTO)) { + if (webPage.photo != null) { boolean smallImage = webPage.type != null && (webPage.type.equals("app") || webPage.type.equals("profile") || webPage.type.equals("article")); if (smallImage && descriptionLayout != null && descriptionLayout.getLineCount() == 1) { smallImage = false; isSmallImage = false; } int maxPhotoWidth = smallImage ? AndroidUtilities.dp(48) : linkPreviewMaxWidth; - currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, maxPhotoWidth); - currentPhotoObjectThumb = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, 80); + TLRPC.PhotoSize currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, maxPhotoWidth, true); + TLRPC.PhotoSize currentPhotoObjectThumb = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, 80); if (currentPhotoObjectThumb == currentPhotoObject) { currentPhotoObjectThumb = null; } @@ -571,8 +568,10 @@ public class ChatMessageCell extends ChatBaseCell { float scale = width / (float) maxPhotoWidth; width /= scale; height /= scale; - if (height > AndroidUtilities.displaySize.y / 3) { - height = AndroidUtilities.displaySize.y / 3; + if (webPage.site_name != null && !webPage.site_name.toLowerCase().equals("instagram")) { + if (height > AndroidUtilities.displaySize.y / 3) { + height = AndroidUtilities.displaySize.y / 3; + } } } if (isSmallImage) { @@ -587,7 +586,26 @@ public class ChatMessageCell extends ChatBaseCell { } linkImageView.setImageCoords(0, 0, width, height); - linkImageView.setImage(currentPhotoObject.location, String.format(Locale.US, "%d_%d", width, height), currentPhotoObjectThumb != null ? currentPhotoObjectThumb.location : null, String.format(Locale.US, "%d_%d_b", width, height), 0, false); + + String fileName = FileLoader.getAttachFileName(currentPhotoObject); + + boolean photoExist = true; + File cacheFile = FileLoader.getPathToAttach(currentPhotoObject, true); + if (!cacheFile.exists()) { + photoExist = false; + } + + String filter = String.format(Locale.US, "%d_%d", width, height); + + if (photoExist || MediaController.getInstance().canDownloadMedia(MediaController.AUTODOWNLOAD_MASK_PHOTO) || FileLoader.getInstance().isLoadingFile(fileName)) { + linkImageView.setImage(currentPhotoObject.location, filter, currentPhotoObjectThumb != null ? currentPhotoObjectThumb.location : null, String.format(Locale.US, "%d_%d_b", width, height), 0, null, false); + } else { + if (currentPhotoObjectThumb != null) { + linkImageView.setImage(null, null, currentPhotoObjectThumb.location, String.format(Locale.US, "%d_%d_b", width, height), 0, null, false); + } else { + linkImageView.setImageBitmap((Drawable) null); + } + } drawLinkImageView = true; if (webPage.site_name != null) { @@ -709,7 +727,7 @@ public class ChatMessageCell extends ChatBaseCell { replyNamePaint.setColor(0xff000000); smallImageStartY = linkPreviewY - AndroidUtilities.dp(1); canvas.save(); - canvas.translate(textX + AndroidUtilities.dp(10), linkPreviewY - AndroidUtilities.dp(3)); + canvas.translate(textX + AndroidUtilities.dp(10) + titleX, linkPreviewY - AndroidUtilities.dp(3)); titleLayout.draw(canvas); canvas.restore(); linkPreviewY += titleLayout.getLineBottom(titleLayout.getLineCount() - 1); @@ -724,7 +742,7 @@ public class ChatMessageCell extends ChatBaseCell { } replyNamePaint.setColor(0xff000000); canvas.save(); - canvas.translate(textX + AndroidUtilities.dp(10), linkPreviewY - AndroidUtilities.dp(3)); + canvas.translate(textX + AndroidUtilities.dp(10) + authorX, linkPreviewY - AndroidUtilities.dp(3)); authorLayout.draw(canvas); canvas.restore(); linkPreviewY += authorLayout.getLineBottom(authorLayout.getLineCount() - 1); @@ -740,7 +758,7 @@ public class ChatMessageCell extends ChatBaseCell { replyTextPaint.setColor(0xff000000); descriptionY = linkPreviewY - AndroidUtilities.dp(3); canvas.save(); - canvas.translate(textX + AndroidUtilities.dp(10), descriptionY); + canvas.translate(textX + AndroidUtilities.dp(10) + descriptionX, descriptionY); if (pressedLink != null && linkBlockNum == -10) { canvas.drawPath(urlPath, urlPaint); } @@ -755,7 +773,7 @@ public class ChatMessageCell extends ChatBaseCell { } if (isSmallImage) { - linkImageView.setImageCoords(textX + smallImageX + AndroidUtilities.dp(12), smallImageStartY, linkImageView.getImageWidth(), linkImageView.getImageHeight()); + linkImageView.setImageCoords(textX + backgroundWidth - AndroidUtilities.dp(77), smallImageStartY, linkImageView.getImageWidth(), linkImageView.getImageHeight()); } else { linkImageView.setImageCoords(textX + AndroidUtilities.dp(10), linkPreviewY, linkImageView.getImageWidth(), linkImageView.getImageHeight()); } @@ -771,8 +789,8 @@ public class ChatMessageCell extends ChatBaseCell { if (durationLayout != null) { int x = linkImageView.getImageX() + linkImageView.getImageWidth() - AndroidUtilities.dp(8) - durationWidth; int y = linkImageView.getImageY() + linkImageView.getImageHeight() - AndroidUtilities.dp(19); - mediaBackgroundDrawable.setBounds(x - AndroidUtilities.dp(4), y - AndroidUtilities.dp(1.5f), x + durationWidth + AndroidUtilities.dp(4), y + AndroidUtilities.dp(14.5f)); - mediaBackgroundDrawable.draw(canvas); + ResourceLoader.mediaBackgroundDrawable.setBounds(x - AndroidUtilities.dp(4), y - AndroidUtilities.dp(1.5f), x + durationWidth + AndroidUtilities.dp(4), y + AndroidUtilities.dp(14.5f)); + ResourceLoader.mediaBackgroundDrawable.draw(canvas); canvas.save(); canvas.translate(x, y); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java index 85f79f6bf..41a469e9e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java @@ -12,10 +12,12 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.drawable.Drawable; +import android.os.Build; import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; +import android.view.MotionEvent; import org.telegram.android.AndroidUtilities; import org.telegram.PhoneFormat.PhoneFormat; @@ -52,6 +54,7 @@ public class DialogCell extends BaseCell { private static Drawable muteDrawable; private static Paint linePaint; + private static Paint backPaint; private long currentDialogId; private boolean isDialogCell; @@ -74,7 +77,6 @@ public class DialogCell extends BaseCell { public boolean useSeparator = false; - private int nameLeft; private StaticLayout nameLayout; private boolean drawNameLock; @@ -111,7 +113,11 @@ public class DialogCell extends BaseCell { private int avatarTop = AndroidUtilities.dp(10); - private void init() { + private boolean isSelected; + + public DialogCell(Context context) { + super(context); + if (namePaint == null) { namePaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG); namePaint.setTextSize(AndroidUtilities.dp(17)); @@ -136,6 +142,9 @@ public class DialogCell extends BaseCell { linePaint = new Paint(); linePaint.setColor(0xffdcdcdc); + backPaint = new Paint(); + backPaint.setColor(0x0f000000); + messagePrintingPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG); messagePrintingPaint.setTextSize(AndroidUtilities.dp(16)); messagePrintingPaint.setColor(0xff4d83b3); @@ -159,11 +168,9 @@ public class DialogCell extends BaseCell { broadcastDrawable = getResources().getDrawable(R.drawable.list_broadcast); muteDrawable = getResources().getDrawable(R.drawable.mute_grey); } - } - public DialogCell(Context context) { - super(context); - init(); + setBackgroundResource(R.drawable.list_selector); + avatarImage = new ImageReceiver(this); avatarImage.setRoundRadius(AndroidUtilities.dp(26)); avatarDrawable = new AvatarDrawable(); @@ -197,14 +204,18 @@ public class DialogCell extends BaseCell { @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - if (avatarImage != null) { - avatarImage.clearImage(); - } + avatarImage.onDetachedFromWindow(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + avatarImage.onAttachedToWindow(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), AndroidUtilities.dp(72)); + setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), AndroidUtilities.dp(72) + (useSeparator ? 1 : 0)); } @Override @@ -218,6 +229,16 @@ public class DialogCell extends BaseCell { } } + @Override + public boolean onTouchEvent(MotionEvent event) { + if (Build.VERSION.SDK_INT >= 21 && getBackground() != null) { + if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE) { + getBackground().setHotspot(event.getX(), event.getY()); + } + } + return super.onTouchEvent(event); + } + public void buildLayout() { String nameString = ""; String timeString = ""; @@ -341,23 +362,36 @@ public class DialogCell extends BaseCell { } } checkMessage = false; - if (message.messageOwner.media != null && !message.isMediaEmpty()) { - currentMessagePaint = messagePrintingPaint; - messageString = Emoji.replaceEmoji(AndroidUtilities.replaceTags(String.format("%s: %s", name, message.messageText)), messagePaint.getFontMetricsInt(), AndroidUtilities.dp(20)); + if (message.caption != null) { + String mess = message.caption.toString(); + if (mess.length() > 150) { + mess = mess.substring(0, 150); + } + mess = mess.replace("\n", " "); + messageString = Emoji.replaceEmoji(AndroidUtilities.replaceTags(String.format("%s: %s", name, mess)), messagePaint.getFontMetricsInt(), AndroidUtilities.dp(20)); } else { - if (message.messageOwner.message != null) { - String mess = message.messageOwner.message; - if (mess.length() > 150) { - mess = mess.substring(0, 150); + if (message.messageOwner.media != null && !message.isMediaEmpty()) { + currentMessagePaint = messagePrintingPaint; + messageString = Emoji.replaceEmoji(AndroidUtilities.replaceTags(String.format("%s: %s", name, message.messageText)), messagePaint.getFontMetricsInt(), AndroidUtilities.dp(20)); + } else { + if (message.messageOwner.message != null) { + String mess = message.messageOwner.message; + if (mess.length() > 150) { + mess = mess.substring(0, 150); + } + mess = mess.replace("\n", " "); + messageString = Emoji.replaceEmoji(AndroidUtilities.replaceTags(String.format("%s: %s", name, mess)), messagePaint.getFontMetricsInt(), AndroidUtilities.dp(20)); } - mess = mess.replace("\n", " "); - messageString = Emoji.replaceEmoji(AndroidUtilities.replaceTags(String.format("%s: %s", name, mess.replace("<", "<").replace(">", ">"))), messagePaint.getFontMetricsInt(), AndroidUtilities.dp(20)); } } } else { - messageString = message.messageText; - if (message.messageOwner.media != null && !message.isMediaEmpty()) { - currentMessagePaint = messagePrintingPaint; + if (message.caption != null) { + messageString = message.caption; + } else { + messageString = message.messageText; + if (message.messageOwner.media != null && !message.isMediaEmpty()) { + currentMessagePaint = messagePrintingPaint; + } } } } @@ -551,8 +585,8 @@ public class DialogCell extends BaseCell { FileLog.e("tmessages", e); } - double widthpx = 0; - float left = 0; + double widthpx; + float left; if (LocaleController.isRTL) { if (nameLayout != null && nameLayout.getLineCount() > 0) { left = nameLayout.getLineLeft(0); @@ -600,6 +634,13 @@ public class DialogCell extends BaseCell { } } + public void setDialogSelected(boolean value) { + if (isSelected != value) { + invalidate(); + } + isSelected = value; + } + public void checkCurrentDialogIndex() { TLRPC.TL_dialog dialog = null; if (isServerOnly) { @@ -635,10 +676,12 @@ public class DialogCell extends BaseCell { if (mask != 0) { boolean continueUpdate = false; - if (isDialogCell && (mask & MessagesController.UPDATE_MASK_USER_PRINT) != 0) { - CharSequence printString = MessagesController.getInstance().printingStrings.get(currentDialogId); - if (lastPrintString != null && printString == null || lastPrintString == null && printString != null || lastPrintString != null && printString != null && !lastPrintString.equals(printString)) { - continueUpdate = true; + if (isDialogCell) { + if ((mask & MessagesController.UPDATE_MASK_USER_PRINT) != 0) { + CharSequence printString = MessagesController.getInstance().printingStrings.get(currentDialogId); + if (lastPrintString != null && printString == null || lastPrintString == null && printString != null || lastPrintString != null && printString != null && !lastPrintString.equals(printString)) { + continueUpdate = true; + } } } if (!continueUpdate && (mask & MessagesController.UPDATE_MASK_AVATAR) != 0) { @@ -721,7 +764,7 @@ public class DialogCell extends BaseCell { } avatarDrawable.setInfo(chat); } - avatarImage.setImage(photo, "50_50", avatarDrawable, false); + avatarImage.setImage(photo, "50_50", avatarDrawable, null, false); if (getMeasuredWidth() != 0 || getMeasuredHeight() != 0) { buildLayout(); @@ -738,6 +781,10 @@ public class DialogCell extends BaseCell { return; } + if (isSelected) { + canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), backPaint); + } + if (drawNameLock) { setDrawableBounds(lockDrawable, nameLockLeft, nameLockTop); lockDrawable.draw(canvas); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerActionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerActionCell.java index 2a4d708fc..49c9c5514 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerActionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerActionCell.java @@ -15,6 +15,7 @@ import android.widget.FrameLayout; import android.widget.TextView; import org.telegram.android.AndroidUtilities; +import org.telegram.ui.Components.LayoutHelper; public class DrawerActionCell extends FrameLayout { @@ -32,14 +33,7 @@ public class DrawerActionCell extends FrameLayout { textView.setSingleLine(true); textView.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL); textView.setCompoundDrawablePadding(AndroidUtilities.dp(34)); - addView(textView); - LayoutParams layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; - layoutParams.gravity = Gravity.LEFT; - layoutParams.leftMargin = AndroidUtilities.dp(14); - layoutParams.rightMargin = AndroidUtilities.dp(16); - textView.setLayoutParams(layoutParams); + addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 14, 0, 16, 0)); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerProfileCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerProfileCell.java index 706323fd6..1d2692c6a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerProfileCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerProfileCell.java @@ -27,10 +27,12 @@ import org.telegram.PhoneFormat.PhoneFormat; import org.telegram.android.AndroidUtilities; import org.telegram.android.ContactsController; import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.FileLog; import org.telegram.messenger.R; import org.telegram.messenger.TLRPC; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.LayoutHelper; public class DrawerProfileCell extends FrameLayout { @@ -50,23 +52,11 @@ public class DrawerProfileCell extends FrameLayout { shadowView.setVisibility(INVISIBLE); shadowView.setScaleType(ImageView.ScaleType.FIT_XY); shadowView.setImageResource(R.drawable.bottom_shadow); - addView(shadowView); - LayoutParams layoutParams = (FrameLayout.LayoutParams) shadowView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = AndroidUtilities.dp(70); - layoutParams.gravity = Gravity.LEFT | Gravity.BOTTOM; - shadowView.setLayoutParams(layoutParams); + addView(shadowView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 70, Gravity.LEFT | Gravity.BOTTOM)); avatarImageView = new BackupImageView(context); avatarImageView.getImageReceiver().setRoundRadius(AndroidUtilities.dp(32)); - addView(avatarImageView); - layoutParams = (LayoutParams) avatarImageView.getLayoutParams(); - layoutParams.width = AndroidUtilities.dp(64); - layoutParams.height = AndroidUtilities.dp(64); - layoutParams.gravity = Gravity.LEFT | Gravity.BOTTOM; - layoutParams.leftMargin = AndroidUtilities.dp(16); - layoutParams.bottomMargin = AndroidUtilities.dp(67); - avatarImageView.setLayoutParams(layoutParams); + addView(avatarImageView, LayoutHelper.createFrame(64, 64, Gravity.LEFT | Gravity.BOTTOM, 16, 0, 0, 67)); nameTextView = new TextView(context); nameTextView.setTextColor(0xffffffff); @@ -76,15 +66,7 @@ public class DrawerProfileCell extends FrameLayout { nameTextView.setMaxLines(1); nameTextView.setSingleLine(true); nameTextView.setGravity(Gravity.LEFT); - addView(nameTextView); - layoutParams = (FrameLayout.LayoutParams) nameTextView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.LEFT | Gravity.BOTTOM; - layoutParams.leftMargin = AndroidUtilities.dp(16); - layoutParams.bottomMargin = AndroidUtilities.dp(28); - layoutParams.rightMargin = AndroidUtilities.dp(16); - nameTextView.setLayoutParams(layoutParams); + addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.BOTTOM, 16, 0, 16, 28)); phoneTextView = new TextView(context); phoneTextView.setTextColor(0xffc2e5ff); @@ -93,15 +75,7 @@ public class DrawerProfileCell extends FrameLayout { phoneTextView.setMaxLines(1); phoneTextView.setSingleLine(true); phoneTextView.setGravity(Gravity.LEFT); - addView(phoneTextView); - layoutParams = (FrameLayout.LayoutParams) phoneTextView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.LEFT | Gravity.BOTTOM; - layoutParams.leftMargin = AndroidUtilities.dp(16); - layoutParams.bottomMargin = AndroidUtilities.dp(9); - layoutParams.rightMargin = AndroidUtilities.dp(16); - phoneTextView.setLayoutParams(layoutParams); + addView(phoneTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.BOTTOM, 16, 0, 16, 9)); } @Override @@ -109,7 +83,11 @@ public class DrawerProfileCell extends FrameLayout { if (Build.VERSION.SDK_INT >= 21) { super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(148) + AndroidUtilities.statusBarHeight, MeasureSpec.EXACTLY)); } else { - super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(148), MeasureSpec.EXACTLY)); + try { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(148), MeasureSpec.EXACTLY)); + } catch (Exception e) { + FileLog.e("tmessages", e); + } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/EmptyCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/EmptyCell.java index fd06f4e8e..0458b98f2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/EmptyCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/EmptyCell.java @@ -11,8 +11,6 @@ package org.telegram.ui.Cells; import android.content.Context; import android.widget.FrameLayout; -import org.telegram.android.AndroidUtilities; - public class EmptyCell extends FrameLayout { int cellHeight; @@ -33,6 +31,6 @@ public class EmptyCell extends FrameLayout { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(cellHeight), MeasureSpec.EXACTLY)); + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(cellHeight, MeasureSpec.EXACTLY)); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/GreySectionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/GreySectionCell.java index 633e02c8e..c38def2a2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/GreySectionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/GreySectionCell.java @@ -16,6 +16,7 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; +import org.telegram.ui.Components.LayoutHelper; public class GreySectionCell extends FrameLayout { private TextView textView; @@ -30,19 +31,12 @@ public class GreySectionCell extends FrameLayout { textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); textView.setTextColor(0xff8a8a8a); textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); - addView(textView); - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)textView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.leftMargin = AndroidUtilities.dp(16); - layoutParams.rightMargin = AndroidUtilities.dp(16); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - textView.setLayoutParams(layoutParams); + addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 16, 0, 16, 0)); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(36), MeasureSpec.EXACTLY)); + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(36), MeasureSpec.EXACTLY)); } public void setText(String text) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/HashtagSearchCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/HashtagSearchCell.java index ccb8dbcef..6655015ef 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/HashtagSearchCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/HashtagSearchCell.java @@ -11,11 +11,14 @@ package org.telegram.ui.Cells; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; +import android.os.Build; import android.util.TypedValue; import android.view.Gravity; +import android.view.MotionEvent; import android.widget.TextView; import org.telegram.android.AndroidUtilities; +import org.telegram.messenger.R; public class HashtagSearchCell extends TextView { @@ -32,6 +35,18 @@ public class HashtagSearchCell extends TextView { paint = new Paint(); paint.setColor(0xffdcdcdc); } + + setBackgroundResource(R.drawable.list_selector); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (Build.VERSION.SDK_INT >= 21 && getBackground() != null) { + if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE) { + getBackground().setHotspot(event.getX(), event.getY()); + } + } + return super.onTouchEvent(event); } public void setNeedDivider(boolean value) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/HeaderCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/HeaderCell.java index fcfd8b2e0..bfc725fa0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/HeaderCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/HeaderCell.java @@ -9,7 +9,6 @@ package org.telegram.ui.Cells; import android.content.Context; -import android.util.AttributeSet; import android.util.TypedValue; import android.view.Gravity; import android.widget.FrameLayout; @@ -17,46 +16,21 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; +import org.telegram.ui.Components.LayoutHelper; public class HeaderCell extends FrameLayout { private TextView textView; - private void init() { + public HeaderCell(Context context) { + super(context); + textView = new TextView(getContext()); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); textView.setTextColor(0xff3e90cf); textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); - addView(textView); - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)textView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.leftMargin = AndroidUtilities.dp(17); - layoutParams.rightMargin = AndroidUtilities.dp(17); - layoutParams.topMargin = AndroidUtilities.dp(15); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - textView.setLayoutParams(layoutParams); - } - - public HeaderCell(Context context) { - super(context); - init(); - } - - public HeaderCell(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - public HeaderCell(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(); - } - - public HeaderCell(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - init(); + addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 17, 15, 17, 0)); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LetterSectionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LetterSectionCell.java index 40e5b3a1f..4ff29231f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LetterSectionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LetterSectionCell.java @@ -16,6 +16,7 @@ import android.widget.FrameLayout; import android.widget.TextView; import org.telegram.android.AndroidUtilities; +import org.telegram.ui.Components.LayoutHelper; public class LetterSectionCell extends FrameLayout { @@ -30,11 +31,7 @@ public class LetterSectionCell extends FrameLayout { textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); textView.setTextColor(0xff808080); textView.setGravity(Gravity.CENTER); - addView(textView); - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)textView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; - textView.setLayoutParams(layoutParams); + addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); } public void setLetter(String letter) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LoadingCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LoadingCell.java index a98c5a0c2..fe1f545c0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LoadingCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LoadingCell.java @@ -14,6 +14,7 @@ import android.widget.FrameLayout; import android.widget.ProgressBar; import org.telegram.android.AndroidUtilities; +import org.telegram.ui.Components.LayoutHelper; public class LoadingCell extends FrameLayout { @@ -21,16 +22,11 @@ public class LoadingCell extends FrameLayout { super(context); ProgressBar progressBar = new ProgressBar(context); - addView(progressBar); - LayoutParams layoutParams = (FrameLayout.LayoutParams) progressBar.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.CENTER; - progressBar.setLayoutParams(layoutParams); + addView(progressBar, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(54), MeasureSpec.EXACTLY)); + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(54), MeasureSpec.EXACTLY)); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationCell.java new file mode 100644 index 000000000..015083117 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationCell.java @@ -0,0 +1,91 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Cells; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.Gravity; +import android.widget.FrameLayout; +import android.widget.TextView; + +import org.telegram.android.AndroidUtilities; +import org.telegram.android.LocaleController; +import org.telegram.messenger.R; +import org.telegram.messenger.TLRPC; +import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.LayoutHelper; + +public class LocationCell extends FrameLayout { + + private TextView nameTextView; + private TextView addressTextView; + private BackupImageView imageView; + private boolean needDivider; + private static Paint paint; + + public LocationCell(Context context) { + super(context); + + if (paint == null) { + paint = new Paint(); + paint.setColor(0xffd9d9d9); + paint.setStrokeWidth(1); + } + + imageView = new BackupImageView(context); + imageView.setBackgroundResource(R.drawable.round_grey); + imageView.setSize(AndroidUtilities.dp(30), AndroidUtilities.dp(30)); + imageView.getImageReceiver().setColorFilter(new PorterDuffColorFilter(0xff999999, PorterDuff.Mode.MULTIPLY)); + addView(imageView, LayoutHelper.createFrame(40, 40, Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), LocaleController.isRTL ? 0 : 17, 8, LocaleController.isRTL ? 17 : 0, 0)); + + nameTextView = new TextView(context); + nameTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + nameTextView.setMaxLines(1); + nameTextView.setEllipsize(TextUtils.TruncateAt.END); + nameTextView.setSingleLine(true); + nameTextView.setTextColor(0xff212121); + nameTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + nameTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), (LocaleController.isRTL ? 16 : 72), 5, (LocaleController.isRTL ? 72 : 16), 0)); + + addressTextView = new TextView(context); + addressTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + addressTextView.setMaxLines(1); + addressTextView.setEllipsize(TextUtils.TruncateAt.END); + addressTextView.setSingleLine(true); + addressTextView.setTextColor(0xff999999); + addressTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + addView(addressTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), (LocaleController.isRTL ? 16 : 72), 30, (LocaleController.isRTL ? 72 : 16), 0)); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(56) + (needDivider ? 1 : 0), MeasureSpec.EXACTLY)); + } + + public void setLocation(TLRPC.TL_messageMediaVenue location, String icon, boolean divider) { + needDivider = divider; + nameTextView.setText(location.title); + addressTextView.setText(location.address); + imageView.setImage(icon, null, null); + setWillNotDraw(!divider); + } + + @Override + protected void onDraw(Canvas canvas) { + if (needDivider) { + canvas.drawLine(AndroidUtilities.dp(72), getHeight() - 1, getWidth(), getHeight() - 1, paint); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationLoadingCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationLoadingCell.java new file mode 100644 index 000000000..0b917d844 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationLoadingCell.java @@ -0,0 +1,50 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Cells; + +import android.content.Context; +import android.util.TypedValue; +import android.view.Gravity; +import android.widget.FrameLayout; +import android.widget.ProgressBar; +import android.widget.TextView; + +import org.telegram.android.AndroidUtilities; +import org.telegram.android.LocaleController; +import org.telegram.messenger.R; +import org.telegram.ui.Components.LayoutHelper; + +public class LocationLoadingCell extends FrameLayout { + + private ProgressBar progressBar; + private TextView textView; + + public LocationLoadingCell(Context context) { + super(context); + + progressBar = new ProgressBar(context); + addView(progressBar, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); + + textView = new TextView(context); + textView.setTextColor(0xff999999); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + textView.setText(LocaleController.getString("NoResult", R.string.NoResult)); + addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec((int) (AndroidUtilities.dp(56) * 2.5f), MeasureSpec.EXACTLY)); + } + + public void setLoading(boolean value) { + progressBar.setVisibility(value ? VISIBLE : INVISIBLE); + textView.setVisibility(value ? INVISIBLE : VISIBLE); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationPoweredCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationPoweredCell.java new file mode 100644 index 000000000..3009f6094 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationPoweredCell.java @@ -0,0 +1,53 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Cells; + +import android.content.Context; +import android.util.TypedValue; +import android.view.Gravity; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import org.telegram.android.AndroidUtilities; +import org.telegram.messenger.R; +import org.telegram.ui.Components.LayoutHelper; + +public class LocationPoweredCell extends FrameLayout { + + public LocationPoweredCell(Context context) { + super(context); + + LinearLayout linearLayout = new LinearLayout(context); + addView(linearLayout, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); + + TextView textView = new TextView(context); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + textView.setTextColor(0xff999999); + textView.setText("Powered by"); + linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); + + ImageView imageView = new ImageView(context); + imageView.setImageResource(R.drawable.foursquare); + imageView.setPadding(0, AndroidUtilities.dp(2), 0, 0); + linearLayout.addView(imageView, LayoutHelper.createLinear(35, LayoutHelper.WRAP_CONTENT)); + + textView = new TextView(context); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + textView.setTextColor(0xff999999); + textView.setText("Foursquare"); + linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(56), MeasureSpec.EXACTLY)); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/MentionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/MentionCell.java index 67111a811..7a477eafb 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/MentionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/MentionCell.java @@ -20,6 +20,7 @@ import org.telegram.android.ContactsController; import org.telegram.messenger.TLRPC; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.LayoutHelper; public class MentionCell extends LinearLayout { @@ -38,13 +39,7 @@ public class MentionCell extends LinearLayout { imageView = new BackupImageView(context); imageView.setRoundRadius(AndroidUtilities.dp(14)); - addView(imageView); - LayoutParams layoutParams = (LayoutParams) imageView.getLayoutParams(); - layoutParams.leftMargin = AndroidUtilities.dp(12); - layoutParams.topMargin = AndroidUtilities.dp(4); - layoutParams.width = AndroidUtilities.dp(28); - layoutParams.height = AndroidUtilities.dp(28); - imageView.setLayoutParams(layoutParams); + addView(imageView, LayoutHelper.createLinear(28, 28, 12, 4, 0, 0)); nameTextView = new TextView(context); nameTextView.setTextColor(0xff000000); @@ -52,13 +47,7 @@ public class MentionCell extends LinearLayout { nameTextView.setSingleLine(true); nameTextView.setGravity(Gravity.LEFT); nameTextView.setEllipsize(TextUtils.TruncateAt.END); - addView(nameTextView); - layoutParams = (LayoutParams) nameTextView.getLayoutParams(); - layoutParams.leftMargin = AndroidUtilities.dp(12); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.CENTER_VERTICAL; - nameTextView.setLayoutParams(layoutParams); + addView(nameTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL, 12, 0, 0, 0)); usernameTextView = new TextView(context); usernameTextView.setTextColor(0xff999999); @@ -66,13 +55,7 @@ public class MentionCell extends LinearLayout { usernameTextView.setSingleLine(true); usernameTextView.setGravity(Gravity.LEFT); usernameTextView.setEllipsize(TextUtils.TruncateAt.END); - addView(usernameTextView); - layoutParams = (LayoutParams) usernameTextView.getLayoutParams(); - layoutParams.leftMargin = AndroidUtilities.dp(12); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.CENTER_VERTICAL; - usernameTextView.setLayoutParams(layoutParams); + addView(usernameTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL, 12, 0, 0, 0)); } @Override @@ -104,4 +87,14 @@ public class MentionCell extends LinearLayout { usernameTextView.setVisibility(INVISIBLE); nameTextView.setText(text); } + + public void setIsDarkTheme(boolean isDarkTheme) { + if (isDarkTheme) { + nameTextView.setTextColor(0xffffffff); + usernameTextView.setTextColor(0xff999999); + } else { + nameTextView.setTextColor(0xff000000); + usernameTextView.setTextColor(0xff999999); + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoEditToolCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoEditToolCell.java index 25d67fe46..55d39ec94 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoEditToolCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoEditToolCell.java @@ -17,6 +17,7 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.ui.Components.FrameLayoutFixed; +import org.telegram.ui.Components.LayoutHelper; public class PhotoEditToolCell extends FrameLayoutFixed { @@ -29,12 +30,7 @@ public class PhotoEditToolCell extends FrameLayoutFixed { iconImage = new ImageView(context); iconImage.setScaleType(ImageView.ScaleType.CENTER); - addView(iconImage); - LayoutParams layoutParams = (LayoutParams) iconImage.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; - layoutParams.bottomMargin = AndroidUtilities.dp(12); - iconImage.setLayoutParams(layoutParams); + addView(iconImage, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT, 0, 0, 0, 12)); nameTextView = new TextView(context); nameTextView.setGravity(Gravity.CENTER); @@ -44,27 +40,13 @@ public class PhotoEditToolCell extends FrameLayoutFixed { nameTextView.setMaxLines(1); nameTextView.setSingleLine(true); nameTextView.setEllipsize(TextUtils.TruncateAt.END); - addView(nameTextView); - layoutParams = (LayoutParams) nameTextView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.LEFT | Gravity.BOTTOM; - layoutParams.leftMargin = AndroidUtilities.dp(4); - layoutParams.rightMargin = AndroidUtilities.dp(4); - nameTextView.setLayoutParams(layoutParams); + addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.BOTTOM, 4, 0, 4, 0)); valueTextView = new TextView(context); valueTextView.setTextColor(0xff6cc3ff); valueTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 11); valueTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - addView(valueTextView); - layoutParams = (LayoutParams) valueTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.LEFT | Gravity.TOP; - layoutParams.leftMargin = AndroidUtilities.dp(57); - layoutParams.topMargin = AndroidUtilities.dp(3); - valueTextView.setLayoutParams(layoutParams); + addView(valueTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 57, 3, 0, 0)); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerAlbumsCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerAlbumsCell.java index 16540b5da..caff290ca 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerAlbumsCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerAlbumsCell.java @@ -23,6 +23,7 @@ import org.telegram.android.MediaController; import org.telegram.messenger.R; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.FrameLayoutFixed; +import org.telegram.ui.Components.LayoutHelper; public class PhotoPickerAlbumsCell extends FrameLayoutFixed { @@ -46,21 +47,12 @@ public class PhotoPickerAlbumsCell extends FrameLayoutFixed { super(context); imageView = new BackupImageView(context); - addView(imageView); - LayoutParams layoutParams = (LayoutParams) imageView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; - imageView.setLayoutParams(layoutParams); + addView(imageView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); LinearLayout linearLayout = new LinearLayout(context); linearLayout.setOrientation(LinearLayout.HORIZONTAL); linearLayout.setBackgroundColor(0x7f000000); - addView(linearLayout); - layoutParams = (LayoutParams) linearLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = AndroidUtilities.dp(28); - layoutParams.gravity = Gravity.BOTTOM; - linearLayout.setLayoutParams(layoutParams); + addView(linearLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 28, Gravity.LEFT | Gravity.BOTTOM)); nameTextView = new TextView(context); nameTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); @@ -69,13 +61,7 @@ public class PhotoPickerAlbumsCell extends FrameLayoutFixed { nameTextView.setEllipsize(TextUtils.TruncateAt.END); nameTextView.setMaxLines(1); nameTextView.setGravity(Gravity.CENTER_VERTICAL); - linearLayout.addView(nameTextView); - LinearLayout.LayoutParams layoutParams1 = (LinearLayout.LayoutParams) nameTextView.getLayoutParams(); - layoutParams1.width = 0; - layoutParams1.height = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams1.leftMargin = AndroidUtilities.dp(8); - layoutParams1.weight = 1; - nameTextView.setLayoutParams(layoutParams1); + linearLayout.addView(nameTextView, LayoutHelper.createLinear(0, LayoutHelper.MATCH_PARENT, 1.0f, 8, 0, 0, 0)); countTextView = new TextView(context); countTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); @@ -84,21 +70,11 @@ public class PhotoPickerAlbumsCell extends FrameLayoutFixed { countTextView.setEllipsize(TextUtils.TruncateAt.END); countTextView.setMaxLines(1); countTextView.setGravity(Gravity.CENTER_VERTICAL); - linearLayout.addView(countTextView); - layoutParams1 = (LinearLayout.LayoutParams) countTextView.getLayoutParams(); - layoutParams1.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams1.height = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams1.leftMargin = AndroidUtilities.dp(4); - layoutParams1.rightMargin = AndroidUtilities.dp(4); - countTextView.setLayoutParams(layoutParams1); + linearLayout.addView(countTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, 4, 0, 4, 0)); selector = new View(context); selector.setBackgroundResource(R.drawable.list_selector); - addView(selector); - layoutParams = (LayoutParams) selector.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; - selector.setLayoutParams(layoutParams); + addView(selector, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); } @Override @@ -149,7 +125,11 @@ public class PhotoPickerAlbumsCell extends FrameLayoutFixed { albumView.imageView.setOrientation(0, true); if (albumEntry.coverPhoto != null && albumEntry.coverPhoto.path != null) { albumView.imageView.setOrientation(albumEntry.coverPhoto.orientation, true); - albumView.imageView.setImage("thumb://" + albumEntry.coverPhoto.imageId + ":" + albumEntry.coverPhoto.path, null, getContext().getResources().getDrawable(R.drawable.nophotos)); + if (albumEntry.coverPhoto.isVideo) { + albumView.imageView.setImage("vthumb://" + albumEntry.coverPhoto.imageId + ":" + albumEntry.coverPhoto.path, null, getContext().getResources().getDrawable(R.drawable.nophotos)); + } else { + albumView.imageView.setImage("thumb://" + albumEntry.coverPhoto.imageId + ":" + albumEntry.coverPhoto.path, null, getContext().getResources().getDrawable(R.drawable.nophotos)); + } } else { albumView.imageView.setImageResource(R.drawable.nophotos); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerPhotoCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerPhotoCell.java index f00434920..94f85c207 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerPhotoCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerPhotoCell.java @@ -16,6 +16,7 @@ import org.telegram.android.AndroidUtilities; import org.telegram.messenger.R; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.CheckBox; +import org.telegram.ui.Components.LayoutHelper; public class PhotoPickerPhotoCell extends FrameLayout { @@ -28,33 +29,17 @@ public class PhotoPickerPhotoCell extends FrameLayout { super(context); photoImage = new BackupImageView(context); - addView(photoImage); - LayoutParams layoutParams = (LayoutParams) photoImage.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; - photoImage.setLayoutParams(layoutParams); + addView(photoImage, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); checkFrame = new FrameLayout(context); - addView(checkFrame); - layoutParams = (LayoutParams) checkFrame.getLayoutParams(); - layoutParams.width = AndroidUtilities.dp(42); - layoutParams.height = AndroidUtilities.dp(42); - layoutParams.gravity = Gravity.RIGHT | Gravity.TOP; - checkFrame.setLayoutParams(layoutParams); + addView(checkFrame, LayoutHelper.createFrame(42, 42, Gravity.RIGHT | Gravity.TOP)); checkBox = new CheckBox(context, R.drawable.checkbig); checkBox.setSize(30); checkBox.setCheckOffset(AndroidUtilities.dp(1)); checkBox.setDrawBackground(true); checkBox.setColor(0xff3ccaef); - addView(checkBox); - layoutParams = (LayoutParams) checkBox.getLayoutParams(); - layoutParams.width = AndroidUtilities.dp(30); - layoutParams.height = AndroidUtilities.dp(30); - layoutParams.gravity = Gravity.RIGHT | Gravity.TOP; - layoutParams.topMargin = AndroidUtilities.dp(6); - layoutParams.rightMargin = AndroidUtilities.dp(6); - checkBox.setLayoutParams(layoutParams); + addView(checkBox, LayoutHelper.createFrame(30, 30, Gravity.RIGHT | Gravity.TOP, 0, 6, 6, 0)); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerSearchCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerSearchCell.java index e24419ed1..81c007e45 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerSearchCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerSearchCell.java @@ -23,6 +23,7 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; import org.telegram.messenger.R; +import org.telegram.ui.Components.LayoutHelper; public class PhotoPickerSearchCell extends LinearLayout { @@ -44,20 +45,11 @@ public class PhotoPickerSearchCell extends LinearLayout { selector = new View(context); selector.setBackgroundResource(R.drawable.list_selector); - addView(selector); - FrameLayout.LayoutParams layoutParams1 = (FrameLayout.LayoutParams) selector.getLayoutParams(); - layoutParams1.width = LayoutParams.MATCH_PARENT; - layoutParams1.height = LayoutParams.MATCH_PARENT; - selector.setLayoutParams(layoutParams1); + addView(selector, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); imageView = new ImageView(context); imageView.setScaleType(ImageView.ScaleType.CENTER); - addView(imageView); - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) imageView.getLayoutParams(); - layoutParams.height = AndroidUtilities.dp(48); - layoutParams.width = AndroidUtilities.dp(48); - layoutParams1.gravity = Gravity.LEFT | Gravity.TOP; - imageView.setLayoutParams(layoutParams); + addView(imageView, LayoutHelper.createFrame(48, 48, Gravity.LEFT | Gravity.TOP)); textView1 = new TextView(context); textView1.setGravity(Gravity.CENTER_VERTICAL); @@ -66,15 +58,7 @@ public class PhotoPickerSearchCell extends LinearLayout { textView1.setTextColor(0xffffffff); textView1.setSingleLine(true); textView1.setEllipsize(TextUtils.TruncateAt.END); - addView(textView1); - layoutParams1 = (FrameLayout.LayoutParams) textView1.getLayoutParams(); - layoutParams1.width = LayoutParams.MATCH_PARENT; - layoutParams1.height = LayoutParams.WRAP_CONTENT; - layoutParams1.gravity = Gravity.TOP | Gravity.LEFT; - layoutParams1.rightMargin = AndroidUtilities.dp(4); - layoutParams1.leftMargin = AndroidUtilities.dp(51); - layoutParams1.topMargin = AndroidUtilities.dp(8); - textView1.setLayoutParams(layoutParams1); + addView(textView1, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.LEFT, 51, 8, 4, 0)); textView2 = new TextView(context); textView2.setGravity(Gravity.CENTER_VERTICAL); @@ -83,15 +67,7 @@ public class PhotoPickerSearchCell extends LinearLayout { textView2.setTextColor(0xff666666); textView2.setSingleLine(true); textView2.setEllipsize(TextUtils.TruncateAt.END); - addView(textView2); - layoutParams1 = (FrameLayout.LayoutParams) textView2.getLayoutParams(); - layoutParams1.width = LayoutParams.MATCH_PARENT; - layoutParams1.height = LayoutParams.WRAP_CONTENT; - layoutParams1.gravity = Gravity.TOP | Gravity.LEFT; - layoutParams1.leftMargin = AndroidUtilities.dp(51); - layoutParams1.rightMargin = AndroidUtilities.dp(4); - layoutParams1.topMargin = AndroidUtilities.dp(26); - textView2.setLayoutParams(layoutParams1); + addView(textView2, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.LEFT, 51, 26, 4, 0)); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java index b6ce6a2f8..cc7537594 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java @@ -12,10 +12,12 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.drawable.Drawable; +import android.os.Build; import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; +import android.view.MotionEvent; import org.telegram.PhoneFormat.PhoneFormat; import org.telegram.android.AndroidUtilities; @@ -70,13 +72,7 @@ public class ProfileSearchCell extends BaseCell { public ProfileSearchCell(Context context) { super(context); - init(); - avatarImage = new ImageReceiver(this); - avatarImage.setRoundRadius(AndroidUtilities.dp(26)); - avatarDrawable = new AvatarDrawable(); - } - private void init() { if (namePaint == null) { namePaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG); namePaint.setTextSize(AndroidUtilities.dp(17)); @@ -103,6 +99,20 @@ public class ProfileSearchCell extends BaseCell { lockDrawable = getResources().getDrawable(R.drawable.list_secret); groupDrawable = getResources().getDrawable(R.drawable.list_group); } + + avatarImage = new ImageReceiver(this); + avatarImage.setRoundRadius(AndroidUtilities.dp(26)); + avatarDrawable = new AvatarDrawable(); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (Build.VERSION.SDK_INT >= 21 && getBackground() != null) { + if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE) { + getBackground().setHotspot(event.getX(), event.getY()); + } + } + return super.onTouchEvent(event); } public void setData(TLRPC.User u, TLRPC.Chat c, TLRPC.EncryptedChat ec, CharSequence n, CharSequence s) { @@ -117,10 +127,13 @@ public class ProfileSearchCell extends BaseCell { @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - if (avatarImage != null) { - avatarImage.clearImage(); - lastAvatar = null; - } + avatarImage.onDetachedFromWindow(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + avatarImage.onAttachedToWindow(); } @Override @@ -140,7 +153,7 @@ public class ProfileSearchCell extends BaseCell { } public void buildLayout() { - CharSequence nameString = ""; + CharSequence nameString; TextPaint currentNamePaint; drawNameBroadcast = false; @@ -232,7 +245,7 @@ public class ProfileSearchCell extends BaseCell { onlineLeft = AndroidUtilities.dp(11); } - CharSequence onlineString = ""; + CharSequence onlineString; TextPaint currentOnlinePaint = offlinePaint; if (subLabel != null) { @@ -262,8 +275,8 @@ public class ProfileSearchCell extends BaseCell { avatarImage.setImageCoords(avatarLeft, AndroidUtilities.dp(10), AndroidUtilities.dp(52), AndroidUtilities.dp(52)); - double widthpx = 0; - float left = 0; + double widthpx; + float left; if (LocaleController.isRTL) { if (nameLayout.getLineCount() > 0) { left = nameLayout.getLineLeft(0); @@ -367,7 +380,7 @@ public class ProfileSearchCell extends BaseCell { lastAvatar = photo; - avatarImage.setImage(photo, "50_50", avatarDrawable, false); + avatarImage.setImage(photo, "50_50", avatarDrawable, null, false); if (getMeasuredWidth() != 0 || getMeasuredHeight() != 0) { buildLayout(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SendLocationCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SendLocationCell.java new file mode 100644 index 000000000..46a691978 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SendLocationCell.java @@ -0,0 +1,57 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Cells; + +import android.content.Context; +import android.view.Gravity; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import org.telegram.android.AndroidUtilities; +import org.telegram.android.LocaleController; +import org.telegram.messenger.R; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.SimpleTextView; + +public class SendLocationCell extends FrameLayout { + + private SimpleTextView accurateTextView; + private SimpleTextView titleTextView; + + public SendLocationCell(Context context) { + super(context); + + ImageView imageView = new ImageView(context); + imageView.setImageResource(R.drawable.pin); + addView(imageView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), LocaleController.isRTL ? 0 : 17, 13, LocaleController.isRTL ? 17 : 0, 0)); + + titleTextView = new SimpleTextView(context); + titleTextView.setTextSize(16); + titleTextView.setTextColor(0xff377aae); + titleTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + titleTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + addView(titleTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 20, Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), LocaleController.isRTL ? 16 : 73, 12, LocaleController.isRTL ? 73 : 16, 0)); + + accurateTextView = new SimpleTextView(context); + accurateTextView.setTextSize(14); + accurateTextView.setTextColor(0xff999999); + accurateTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + addView(accurateTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 20, Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), LocaleController.isRTL ? 16 : 73, 37, LocaleController.isRTL ? 73 : 16, 0)); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(66), MeasureSpec.EXACTLY)); + } + + public void setText(String title, String text) { + titleTextView.setText(title); + accurateTextView.setText(text); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SessionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SessionCell.java index 924a851f7..4555b276b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SessionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SessionCell.java @@ -22,6 +22,7 @@ import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; import org.telegram.messenger.R; import org.telegram.messenger.TLRPC; +import org.telegram.ui.Components.LayoutHelper; import java.util.Locale; @@ -46,15 +47,7 @@ public class SessionCell extends FrameLayout { LinearLayout linearLayout = new LinearLayout(context); linearLayout.setOrientation(LinearLayout.HORIZONTAL); linearLayout.setWeightSum(1); - addView(linearLayout); - LayoutParams layoutParams = (LayoutParams) linearLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = AndroidUtilities.dp(30); - layoutParams.leftMargin = AndroidUtilities.dp(17); - layoutParams.rightMargin = AndroidUtilities.dp(17); - layoutParams.topMargin = AndroidUtilities.dp(11); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - linearLayout.setLayoutParams(layoutParams); + addView(linearLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 30, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 17, 11, 11, 0)); nameTextView = new TextView(context); nameTextView.setTextColor(0xff212121); @@ -71,32 +64,13 @@ public class SessionCell extends FrameLayout { onlineTextView.setGravity((LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.TOP); if (LocaleController.isRTL) { - linearLayout.addView(onlineTextView); - linearLayout.addView(nameTextView); + linearLayout.addView(onlineTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 2, 0, 0)); + linearLayout.addView(nameTextView, LayoutHelper.createLinear(0, LayoutHelper.MATCH_PARENT, 1.0f, Gravity.RIGHT | Gravity.TOP, 10, 0, 0, 0)); } else { - linearLayout.addView(nameTextView); - linearLayout.addView(onlineTextView); + linearLayout.addView(nameTextView, LayoutHelper.createLinear(0, LayoutHelper.MATCH_PARENT, 1.0f, Gravity.LEFT | Gravity.TOP, 0, 0, 10, 0)); + linearLayout.addView(onlineTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, Gravity.RIGHT | Gravity.TOP, 0, 2, 0, 0)); } - LinearLayout.LayoutParams layoutParams2 = (LinearLayout.LayoutParams) nameTextView.getLayoutParams(); - layoutParams2.width = 0; - layoutParams2.height = LayoutParams.MATCH_PARENT; - layoutParams2.weight = 1; - if (LocaleController.isRTL) { - layoutParams2.leftMargin = AndroidUtilities.dp(10); - } else { - layoutParams2.rightMargin = AndroidUtilities.dp(10); - } - layoutParams2.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - nameTextView.setLayoutParams(layoutParams2); - - layoutParams2 = (LinearLayout.LayoutParams) onlineTextView.getLayoutParams(); - layoutParams2.width = LayoutParams.WRAP_CONTENT; - layoutParams2.height = LayoutParams.MATCH_PARENT; - layoutParams2.topMargin = AndroidUtilities.dp(2); - layoutParams2.gravity = (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.TOP; - onlineTextView.setLayoutParams(layoutParams2); - detailTextView = new TextView(context); detailTextView.setTextColor(0xff212121); detailTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); @@ -105,15 +79,7 @@ public class SessionCell extends FrameLayout { detailTextView.setSingleLine(true); detailTextView.setEllipsize(TextUtils.TruncateAt.END); detailTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); - addView(detailTextView); - layoutParams = (LayoutParams) detailTextView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.leftMargin = AndroidUtilities.dp(17); - layoutParams.rightMargin = AndroidUtilities.dp(17); - layoutParams.topMargin = AndroidUtilities.dp(36); - layoutParams.gravity = (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP; - detailTextView.setLayoutParams(layoutParams); + addView(detailTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 17, 36, 17, 0)); detailExTextView = new TextView(context); detailExTextView.setTextColor(0xff999999); @@ -123,15 +89,7 @@ public class SessionCell extends FrameLayout { detailExTextView.setSingleLine(true); detailExTextView.setEllipsize(TextUtils.TruncateAt.END); detailExTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); - addView(detailExTextView); - layoutParams = (LayoutParams) detailExTextView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.leftMargin = AndroidUtilities.dp(17); - layoutParams.rightMargin = AndroidUtilities.dp(17); - layoutParams.topMargin = AndroidUtilities.dp(59); - layoutParams.gravity = (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP; - detailExTextView.setLayoutParams(layoutParams); + addView(detailExTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 17, 59, 17, 0)); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ShadowBottomSectionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ShadowBottomSectionCell.java index 39a4dfca2..ed0998b50 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ShadowBottomSectionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ShadowBottomSectionCell.java @@ -9,7 +9,6 @@ package org.telegram.ui.Cells; import android.content.Context; -import android.util.AttributeSet; import android.view.View; import org.telegram.android.AndroidUtilities; @@ -17,28 +16,9 @@ import org.telegram.messenger.R; public class ShadowBottomSectionCell extends View { - private void init() { - setBackgroundResource(R.drawable.greydivider_bottom); - } - public ShadowBottomSectionCell(Context context) { super(context); - init(); - } - - public ShadowBottomSectionCell(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - public ShadowBottomSectionCell(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(); - } - - public ShadowBottomSectionCell(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - init(); + setBackgroundResource(R.drawable.greydivider_bottom); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ShadowSectionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ShadowSectionCell.java index b8c7ce118..ad130f9bd 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ShadowSectionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ShadowSectionCell.java @@ -9,7 +9,6 @@ package org.telegram.ui.Cells; import android.content.Context; -import android.util.AttributeSet; import android.view.View; import org.telegram.android.AndroidUtilities; @@ -17,28 +16,9 @@ import org.telegram.messenger.R; public class ShadowSectionCell extends View { - private void init() { - setBackgroundResource(R.drawable.greydivider); - } - public ShadowSectionCell(Context context) { super(context); - init(); - } - - public ShadowSectionCell(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - public ShadowSectionCell(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(); - } - - public ShadowSectionCell(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - init(); + setBackgroundResource(R.drawable.greydivider); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedDocumentCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedDocumentCell.java index a41172b06..4c2e46a36 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedDocumentCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedDocumentCell.java @@ -28,9 +28,9 @@ import org.telegram.android.MessageObject; import org.telegram.messenger.FileLoader; import org.telegram.messenger.R; import org.telegram.messenger.TLRPC; -import org.telegram.messenger.Utilities; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.CheckBox; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.LineProgressView; import java.io.File; @@ -76,15 +76,7 @@ public class SharedDocumentCell extends FrameLayout implements MediaController.F TAG = MediaController.getInstance().generateObserverTag(); placeholderImabeView = new ImageView(context); - addView(placeholderImabeView); - LayoutParams layoutParams = (LayoutParams) placeholderImabeView.getLayoutParams(); - layoutParams.width = AndroidUtilities.dp(40); - layoutParams.height = AndroidUtilities.dp(40); - layoutParams.leftMargin = LocaleController.isRTL ? 0 : AndroidUtilities.dp(12); - layoutParams.rightMargin = LocaleController.isRTL ? AndroidUtilities.dp(12) : 0; - layoutParams.topMargin = AndroidUtilities.dp(8); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - placeholderImabeView.setLayoutParams(layoutParams); + addView(placeholderImabeView, LayoutHelper.createFrame(40, 40, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 0 : 12, 8, LocaleController.isRTL ? 12 : 0, 0)); extTextView = new TextView(context); extTextView.setTextColor(0xffffffff); @@ -95,17 +87,10 @@ public class SharedDocumentCell extends FrameLayout implements MediaController.F extTextView.setSingleLine(true); extTextView.setGravity(Gravity.CENTER); extTextView.setEllipsize(TextUtils.TruncateAt.END); - addView(extTextView); - layoutParams = (LayoutParams) extTextView.getLayoutParams(); - layoutParams.width = AndroidUtilities.dp(32); - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.topMargin = AndroidUtilities.dp(22); - layoutParams.leftMargin = LocaleController.isRTL ? 0 : AndroidUtilities.dp(16); - layoutParams.rightMargin = LocaleController.isRTL ? AndroidUtilities.dp(16) : 0; - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - extTextView.setLayoutParams(layoutParams); + addView(extTextView, LayoutHelper.createFrame(32, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 0 : 16, 22, LocaleController.isRTL ? 16 : 0, 0)); thumbImageView = new BackupImageView(context); + addView(thumbImageView, LayoutHelper.createFrame(40, 40, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 0 : 12, 8, LocaleController.isRTL ? 12 : 0, 0)); thumbImageView.getImageReceiver().setDelegate(new ImageReceiver.ImageReceiverDelegate() { @Override public void didSetImage(ImageReceiver imageReceiver, boolean set, boolean thumb) { @@ -113,15 +98,6 @@ public class SharedDocumentCell extends FrameLayout implements MediaController.F placeholderImabeView.setVisibility(set ? INVISIBLE : VISIBLE); } }); - addView(thumbImageView); - layoutParams = (LayoutParams) thumbImageView.getLayoutParams(); - layoutParams.width = AndroidUtilities.dp(40); - layoutParams.height = AndroidUtilities.dp(40); - layoutParams.leftMargin = LocaleController.isRTL ? 0 : AndroidUtilities.dp(12); - layoutParams.rightMargin = LocaleController.isRTL ? AndroidUtilities.dp(12) : 0; - layoutParams.topMargin = AndroidUtilities.dp(8); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - thumbImageView.setLayoutParams(layoutParams); nameTextView = new TextView(context); nameTextView.setTextColor(0xff222222); @@ -132,27 +108,11 @@ public class SharedDocumentCell extends FrameLayout implements MediaController.F nameTextView.setSingleLine(true); nameTextView.setEllipsize(TextUtils.TruncateAt.END); nameTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); - addView(nameTextView); - layoutParams = (LayoutParams) nameTextView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.topMargin = AndroidUtilities.dp(5); - layoutParams.leftMargin = LocaleController.isRTL ? AndroidUtilities.dp(8) : AndroidUtilities.dp(72); - layoutParams.rightMargin = LocaleController.isRTL ? AndroidUtilities.dp(72) : AndroidUtilities.dp(8); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - nameTextView.setLayoutParams(layoutParams); + addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 8 : 72, 5, LocaleController.isRTL ? 72 : 8, 0)); statusImageView = new ImageView(context); statusImageView.setVisibility(INVISIBLE); - addView(statusImageView); - layoutParams = (LayoutParams) statusImageView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.topMargin = AndroidUtilities.dp(35); - layoutParams.leftMargin = LocaleController.isRTL ? AndroidUtilities.dp(8) : AndroidUtilities.dp(72); - layoutParams.rightMargin = LocaleController.isRTL ? AndroidUtilities.dp(72) : AndroidUtilities.dp(8); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - statusImageView.setLayoutParams(layoutParams); + addView(statusImageView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 8 : 72, 35, LocaleController.isRTL ? 72 : 8, 0)); dateTextView = new TextView(context); dateTextView.setTextColor(0xff999999); @@ -162,38 +122,14 @@ public class SharedDocumentCell extends FrameLayout implements MediaController.F dateTextView.setSingleLine(true); dateTextView.setEllipsize(TextUtils.TruncateAt.END); dateTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); - addView(dateTextView); - layoutParams = (LayoutParams) dateTextView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.topMargin = AndroidUtilities.dp(30); - layoutParams.leftMargin = LocaleController.isRTL ? AndroidUtilities.dp(8) : AndroidUtilities.dp(72); - layoutParams.rightMargin = LocaleController.isRTL ? AndroidUtilities.dp(72) : AndroidUtilities.dp(8); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - dateTextView.setLayoutParams(layoutParams); + addView(dateTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 8 : 72, 30, LocaleController.isRTL ? 72 : 8, 0)); progressView = new LineProgressView(context); - addView(progressView); - layoutParams = (LayoutParams) progressView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = AndroidUtilities.dp(2); - layoutParams.topMargin = AndroidUtilities.dp(54); - layoutParams.leftMargin = LocaleController.isRTL ? 0 : AndroidUtilities.dp(72); - layoutParams.rightMargin = LocaleController.isRTL ? AndroidUtilities.dp(72) : 0; - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - progressView.setLayoutParams(layoutParams); + addView(progressView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 2, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 0 : 72, 54, LocaleController.isRTL ? 72 : 0, 0)); checkBox = new CheckBox(context, R.drawable.round_check2); checkBox.setVisibility(INVISIBLE); - addView(checkBox); - layoutParams = (LayoutParams) checkBox.getLayoutParams(); - layoutParams.width = AndroidUtilities.dp(22); - layoutParams.height = AndroidUtilities.dp(22); - layoutParams.topMargin = AndroidUtilities.dp(30); - layoutParams.leftMargin = LocaleController.isRTL ? 0 : AndroidUtilities.dp(34); - layoutParams.rightMargin = LocaleController.isRTL ? AndroidUtilities.dp(34) : 0; - layoutParams.gravity = (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); - checkBox.setLayoutParams(layoutParams); + addView(checkBox, LayoutHelper.createFrame(22, 22, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 0 : 34, 30, LocaleController.isRTL ? 34 : 0, 0)); } private int getThumbForNameOrMime(String name, String mime) { @@ -269,7 +205,7 @@ public class SharedDocumentCell extends FrameLayout implements MediaController.F loading = false; if (document != null && document.messageOwner.media != null) { - int idx = -1; + int idx; String name = FileLoader.getDocumentFileName(document.messageOwner.media.document); placeholderImabeView.setVisibility(VISIBLE); extTextView.setVisibility(VISIBLE); @@ -284,7 +220,7 @@ public class SharedDocumentCell extends FrameLayout implements MediaController.F thumbImageView.setImage(document.messageOwner.media.document.thumb.location, "40_40", (Drawable) null); } long date = (long) document.messageOwner.date * 1000; - dateTextView.setText(String.format("%s, %s", Utilities.formatFileSize(document.messageOwner.media.document.size), LocaleController.formatString("formatDateAtTime", R.string.formatDateAtTime, LocaleController.formatterYear.format(new Date(date)), LocaleController.formatterDay.format(new Date(date))))); + dateTextView.setText(String.format("%s, %s", AndroidUtilities.formatFileSize(document.messageOwner.media.document.size), LocaleController.formatString("formatDateAtTime", R.string.formatDateAtTime, LocaleController.formatterYear.format(new Date(date)), LocaleController.formatterDay.format(new Date(date))))); } else { nameTextView.setText(""); extTextView.setText(""); @@ -303,7 +239,7 @@ public class SharedDocumentCell extends FrameLayout implements MediaController.F public void updateFileExistIcon() { if (message != null && message.messageOwner.media != null) { String fileName = null; - File cacheFile = null; + File cacheFile; if (message.messageOwner.attachPath == null || message.messageOwner.attachPath.length() == 0 || !(new File(message.messageOwner.attachPath).exists())) { cacheFile = FileLoader.getPathToMessage(message.messageOwner); if (!cacheFile.exists()) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedMediaSectionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedMediaSectionCell.java index 4b7d29c58..1646b509b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedMediaSectionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedMediaSectionCell.java @@ -16,6 +16,7 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; +import org.telegram.ui.Components.LayoutHelper; public class SharedMediaSectionCell extends FrameLayout { @@ -29,14 +30,7 @@ public class SharedMediaSectionCell extends FrameLayout { textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); textView.setTextColor(0xff222222); textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); - addView(textView); - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)textView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.leftMargin = AndroidUtilities.dp(13); - layoutParams.rightMargin = AndroidUtilities.dp(13); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - textView.setLayoutParams(layoutParams); + addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 13, 0, 13, 0)); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell.java index 7b6085d45..879043844 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell.java @@ -27,6 +27,7 @@ import org.telegram.messenger.TLRPC; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.CheckBox; import org.telegram.ui.Components.FrameLayoutFixed; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.PhotoViewer; public class SharedPhotoVideoCell extends FrameLayoutFixed { @@ -57,63 +58,32 @@ public class SharedPhotoVideoCell extends FrameLayoutFixed { imageView = new BackupImageView(context); imageView.getImageReceiver().setNeedsQualityThumb(true); imageView.getImageReceiver().setShouldGenerateQualityThumb(true); - addView(imageView); - LayoutParams layoutParams = (LayoutParams) imageView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; - imageView.setLayoutParams(layoutParams); + addView(imageView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); videoInfoContainer = new LinearLayout(context); videoInfoContainer.setOrientation(LinearLayout.HORIZONTAL); videoInfoContainer.setBackgroundResource(R.drawable.phototime); videoInfoContainer.setPadding(AndroidUtilities.dp(3), 0, AndroidUtilities.dp(3), 0); videoInfoContainer.setGravity(Gravity.CENTER_VERTICAL); - addView(videoInfoContainer); - layoutParams = (LayoutParams) videoInfoContainer.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = AndroidUtilities.dp(16); - layoutParams.gravity = Gravity.BOTTOM | Gravity.LEFT; - videoInfoContainer.setLayoutParams(layoutParams); + addView(videoInfoContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 16, Gravity.BOTTOM | Gravity.LEFT)); ImageView imageView1 = new ImageView(context); imageView1.setImageResource(R.drawable.ic_video); - videoInfoContainer.addView(imageView1); - LinearLayout.LayoutParams layoutParams1 = (LinearLayout.LayoutParams) imageView1.getLayoutParams(); - layoutParams1.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams1.height = LinearLayout.LayoutParams.WRAP_CONTENT; - imageView1.setLayoutParams(layoutParams1); + videoInfoContainer.addView(imageView1, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); videoTextView = new TextView(context); videoTextView.setTextColor(0xffffffff); videoTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12); videoTextView.setGravity(Gravity.CENTER_VERTICAL); - videoInfoContainer.addView(videoTextView); - layoutParams1 = (LinearLayout.LayoutParams) videoTextView.getLayoutParams(); - layoutParams1.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams1.height = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams1.leftMargin = AndroidUtilities.dp(4); - layoutParams1.gravity = Gravity.CENTER_VERTICAL; - layoutParams1.bottomMargin = AndroidUtilities.dp(1); - videoTextView.setLayoutParams(layoutParams1); + videoInfoContainer.addView(videoTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL, 4, 0, 0, 1)); selector = new View(context); selector.setBackgroundResource(R.drawable.list_selector); - addView(selector); - layoutParams = (LayoutParams) selector.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; - selector.setLayoutParams(layoutParams); + addView(selector, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); checkBox = new CheckBox(context, R.drawable.round_check2); checkBox.setVisibility(INVISIBLE); - addView(checkBox); - layoutParams = (LayoutParams) checkBox.getLayoutParams(); - layoutParams.width = AndroidUtilities.dp(22); - layoutParams.height = AndroidUtilities.dp(22); - layoutParams.gravity = Gravity.RIGHT | Gravity.TOP; - layoutParams.topMargin = AndroidUtilities.dp(6); - layoutParams.rightMargin = AndroidUtilities.dp(6); - checkBox.setLayoutParams(layoutParams); + addView(checkBox, LayoutHelper.createFrame(22, 22, Gravity.RIGHT | Gravity.TOP, 6, 0, 6, 0)); } @Override @@ -212,14 +182,14 @@ public class SharedPhotoVideoCell extends FrameLayoutFixed { photoVideoView.videoTextView.setText(String.format("%d:%02d", minutes, seconds)); if (messageObject.messageOwner.media.video.thumb != null) { TLRPC.FileLocation location = messageObject.messageOwner.media.video.thumb.location; - photoVideoView.imageView.setImage(null, null, null, ApplicationLoader.applicationContext.getResources().getDrawable(R.drawable.photo_placeholder_in), null, location, "b", 0); + photoVideoView.imageView.setImage(null, null, null, ApplicationLoader.applicationContext.getResources().getDrawable(R.drawable.photo_placeholder_in), null, location, "b", null, 0); } else { photoVideoView.imageView.setImageResource(R.drawable.photo_placeholder_in); } } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaPhoto && messageObject.messageOwner.media.photo != null && !messageObject.photoThumbs.isEmpty()) { photoVideoView.videoInfoContainer.setVisibility(INVISIBLE); TLRPC.PhotoSize photoSize = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, 80); - photoVideoView.imageView.setImage(null, null, null, ApplicationLoader.applicationContext.getResources().getDrawable(R.drawable.photo_placeholder_in), null, photoSize.location, "b", 0); + photoVideoView.imageView.setImage(null, null, null, ApplicationLoader.applicationContext.getResources().getDrawable(R.drawable.photo_placeholder_in), null, photoSize.location, "b", null, 0); } else { photoVideoView.videoInfoContainer.setVisibility(INVISIBLE); photoVideoView.imageView.setImageResource(R.drawable.photo_placeholder_in); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerCell.java index 34e04c69b..04e79938f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerCell.java @@ -9,7 +9,6 @@ package org.telegram.ui.Cells; import android.content.Context; -import android.graphics.drawable.Drawable; import android.view.Gravity; import org.telegram.android.AndroidUtilities; @@ -17,6 +16,7 @@ import org.telegram.messenger.R; import org.telegram.messenger.TLRPC; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.FrameLayoutFixed; +import org.telegram.ui.Components.LayoutHelper; public class StickerCell extends FrameLayoutFixed { @@ -27,13 +27,7 @@ public class StickerCell extends FrameLayoutFixed { imageView = new BackupImageView(context); imageView.setAspectFit(true); - addView(imageView); - LayoutParams layoutParams = (LayoutParams) imageView.getLayoutParams(); - layoutParams.width = AndroidUtilities.dp(66); - layoutParams.height = AndroidUtilities.dp(66); - layoutParams.gravity = Gravity.CENTER_HORIZONTAL; - layoutParams.topMargin = AndroidUtilities.dp(5); - imageView.setLayoutParams(layoutParams); + addView(imageView, LayoutHelper.createFrame(66, 66, Gravity.CENTER_HORIZONTAL, 0, 5, 0, 0)); } @Override @@ -52,8 +46,7 @@ public class StickerCell extends FrameLayoutFixed { public void setSticker(TLRPC.Document document, int side) { if (document != null) { - document.thumb.location.ext = "webp"; - imageView.setImage(document.thumb.location, null, (Drawable) null); + imageView.setImage(document.thumb.location, null, "webp", null); } if (side == -1) { setBackgroundResource(R.drawable.stickers_back_left); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerEmojiCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerEmojiCell.java new file mode 100644 index 000000000..ce803d315 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerEmojiCell.java @@ -0,0 +1,81 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Cells; + +import android.content.Context; +import android.util.TypedValue; +import android.view.Gravity; +import android.widget.FrameLayout; +import android.widget.TextView; + +import org.telegram.android.AndroidUtilities; +import org.telegram.android.Emoji; +import org.telegram.android.query.StickersQuery; +import org.telegram.messenger.TLRPC; +import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.LayoutHelper; + +public class StickerEmojiCell extends FrameLayout { + + private BackupImageView imageView; + private TLRPC.Document sticker; + private TextView emojiTextView; + + public StickerEmojiCell(Context context) { + super(context); + + imageView = new BackupImageView(context); + imageView.setAspectFit(true); + addView(imageView, LayoutHelper.createFrame(66, 66, Gravity.CENTER)); + + emojiTextView = new TextView(context); + emojiTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + addView(emojiTextView, LayoutHelper.createFrame(28, 28, Gravity.BOTTOM | Gravity.RIGHT)); + } + + @Override + public void setPressed(boolean pressed) { + if (imageView.getImageReceiver().getPressed() != pressed) { + imageView.getImageReceiver().setPressed(pressed); + imageView.invalidate(); + } + super.setPressed(pressed); + } + + public TLRPC.Document getSticker() { + return sticker; + } + + public void setSticker(TLRPC.Document document, boolean showEmoji) { + if (document != null) { + sticker = document; + imageView.setImage(document.thumb.location, null, "webp", null); + + + if (showEmoji) { + boolean set = false; + for (TLRPC.DocumentAttribute attribute : document.attributes) { + if (attribute instanceof TLRPC.TL_documentAttributeSticker) { + if (attribute.alt != null && attribute.alt.length() > 0) { + emojiTextView.setText(Emoji.replaceEmoji(attribute.alt, emojiTextView.getPaint().getFontMetricsInt(), AndroidUtilities.dp(16))); + set = true; + } + break; + } + } + if (!set) { + emojiTextView.setText(Emoji.replaceEmoji(StickersQuery.getEmojiForSticker(sticker.id), emojiTextView.getPaint().getFontMetricsInt(), AndroidUtilities.dp(16))); + } + emojiTextView.setVisibility(VISIBLE); + } else { + emojiTextView.setVisibility(INVISIBLE); + } + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerSetCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerSetCell.java new file mode 100644 index 000000000..03699a64a --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerSetCell.java @@ -0,0 +1,144 @@ +/* + * This is the source code of Telegram for Android v. 2.x.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2014. + */ + +package org.telegram.ui.Cells; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.Gravity; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; + +import org.telegram.android.AndroidUtilities; +import org.telegram.android.AnimationCompat.ViewProxy; +import org.telegram.android.LocaleController; +import org.telegram.android.query.StickersQuery; +import org.telegram.messenger.R; +import org.telegram.messenger.TLRPC; +import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.LayoutHelper; + +import java.util.ArrayList; + +public class StickerSetCell extends FrameLayout { + + private TextView textView; + private TextView valueTextView; + private BackupImageView imageView; + private boolean needDivider; + private ImageView optionsButton; + private TLRPC.TL_stickerSet stickersSet; + + private static Paint paint; + + public StickerSetCell(Context context) { + super(context); + + if (paint == null) { + paint = new Paint(); + paint.setColor(0xffd9d9d9); + } + + textView = new TextView(context); + textView.setTextColor(0xff212121); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + textView.setLines(1); + textView.setMaxLines(1); + textView.setSingleLine(true); + textView.setEllipsize(TextUtils.TruncateAt.END); + textView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT, LocaleController.isRTL ? 40 : 71, 10, LocaleController.isRTL ? 40 : 71, 0)); + + valueTextView = new TextView(context); + valueTextView.setTextColor(0xff8a8a8a); + valueTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); + valueTextView.setLines(1); + valueTextView.setMaxLines(1); + valueTextView.setSingleLine(true); + valueTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + addView(valueTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT, LocaleController.isRTL ? 40 : 71, 35, LocaleController.isRTL ? 40 : 71, 0)); + + imageView = new BackupImageView(context); + imageView.setAspectFit(true); + addView(imageView, LayoutHelper.createFrame(48, 48, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 0 : 12, 8, LocaleController.isRTL ? 12 : 0, 0)); + + optionsButton = new ImageView(context); + optionsButton.setBackgroundResource(R.drawable.bar_selector_grey); + optionsButton.setImageResource(R.drawable.doc_actions_b); + optionsButton.setScaleType(ImageView.ScaleType.CENTER); + addView(optionsButton, LayoutHelper.createFrame(40, 40, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.TOP)); + + /*ActionBarMenuItem menuItem = new ActionBarMenuItem(context, null, R.drawable.bar_selector_grey); + menuItem.setIcon(R.drawable.doc_actions_b); + addView(menuItem, LayoutHelper.createFrame(40, 40, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.TOP, LocaleController.isRTL ? 40 : 0, 0, LocaleController.isRTL ? 0 : 40, 0)); + menuItem.addSubItem(1, "test", 0); + menuItem.addSubItem(2, "test", 0); + menuItem.addSubItem(3, "test", 0); + menuItem.addSubItem(4, "test", 0); + menuItem.addSubItem(5, "test", 0); + menuItem.addSubItem(6, "test", 0); + menuItem.addSubItem(7, "test", 0);*/ + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(64) + (needDivider ? 1 : 0), MeasureSpec.EXACTLY)); + } + + public void setStickersSet(TLRPC.TL_stickerSet set, boolean divider) { + needDivider = divider; + stickersSet = set; + + if (stickersSet.id == -1) { + textView.setText(LocaleController.getString("GeniusStickerPackName", R.string.GeniusStickerPackName)); + if (StickersQuery.getHideMainStickersPack()) { + ViewProxy.setAlpha(textView, 0.5f); + ViewProxy.setAlpha(valueTextView, 0.5f); + ViewProxy.setAlpha(imageView, 0.5f); + } else { + ViewProxy.setAlpha(textView, 1.0f); + ViewProxy.setAlpha(valueTextView, 1.0f); + ViewProxy.setAlpha(imageView, 1.0f); + } + } else { + textView.setText(stickersSet.title); + ViewProxy.setAlpha(textView, 1.0f); + ViewProxy.setAlpha(valueTextView, 1.0f); + ViewProxy.setAlpha(imageView, 1.0f); + } + ArrayList documents = StickersQuery.getStickersForSet(stickersSet.id); + if (documents != null) { + valueTextView.setText(LocaleController.formatPluralString("Stickers", documents.size())); + TLRPC.Document document = documents.get(0); + if (document.thumb != null && document.thumb.location != null) { + imageView.setImage(document.thumb.location, null, "webp", null); + } + } else { + valueTextView.setText(LocaleController.formatPluralString("Stickers", 0)); + } + } + + public void setOnOptionsClick(OnClickListener listener) { + optionsButton.setOnClickListener(listener); + } + + public TLRPC.TL_stickerSet getStickersSet() { + return stickersSet; + } + + @Override + protected void onDraw(Canvas canvas) { + if (needDivider) { + canvas.drawLine(0, getHeight() - 1, getWidth() - getPaddingRight(), getHeight() - 1, paint); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextBlockCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextBlockCell.java new file mode 100644 index 000000000..15f7b3d13 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextBlockCell.java @@ -0,0 +1,60 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Cells; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.util.TypedValue; +import android.view.Gravity; +import android.widget.FrameLayout; +import android.widget.TextView; + +import org.telegram.android.LocaleController; +import org.telegram.ui.Components.LayoutHelper; + +public class TextBlockCell extends FrameLayout { + + private TextView textView; + private static Paint paint; + private boolean needDivider; + + public TextBlockCell(Context context) { + super(context); + + if (paint == null) { + paint = new Paint(); + paint.setColor(0xffd9d9d9); + paint.setStrokeWidth(1); + } + + textView = new TextView(context); + textView.setTextColor(0xff212121); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); + addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 17, 8, 17, 8)); + } + + public void setTextColor(int color) { + textView.setTextColor(color); + } + + public void setText(String text, boolean divider) { + textView.setText(text); + needDivider = divider; + setWillNotDraw(!divider); + } + + @Override + protected void onDraw(Canvas canvas) { + if (needDivider) { + canvas.drawLine(getPaddingLeft(), getHeight() - 1, getWidth() - getPaddingRight(), getHeight() - 1, paint); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCell.java index d86331002..d43cb7580 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCell.java @@ -19,6 +19,7 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; +import org.telegram.ui.Components.LayoutHelper; public class TextCell extends FrameLayout { @@ -38,14 +39,7 @@ public class TextCell extends FrameLayout { textView.setSingleLine(true); textView.setEllipsize(TextUtils.TruncateAt.END); textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); - addView(textView); - LayoutParams layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; - layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : 71); - layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 71 : 16); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - textView.setLayoutParams(layoutParams); + addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 16 : 71, 0, LocaleController.isRTL ? 71 : 16, 0)); valueTextView = new TextView(context); valueTextView.setTextColor(0xff2f8cc9); @@ -54,36 +48,15 @@ public class TextCell extends FrameLayout { valueTextView.setMaxLines(1); valueTextView.setSingleLine(true); valueTextView.setGravity((LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL); - addView(valueTextView); - layoutParams = (LayoutParams) valueTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.MATCH_PARENT; - layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 24 : 0); - layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 0 : 24); - layoutParams.gravity = LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT; - valueTextView.setLayoutParams(layoutParams); + addView(valueTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.TOP, LocaleController.isRTL ? 24 : 0, 0, LocaleController.isRTL ? 0 : 24, 0)); imageView = new ImageView(context); imageView.setScaleType(ImageView.ScaleType.CENTER); - addView(imageView); - layoutParams = (LayoutParams) imageView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 0 : 16); - layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : 0); - layoutParams.gravity = (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL; - imageView.setLayoutParams(layoutParams); + addView(imageView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL, LocaleController.isRTL ? 0 : 16, 0, LocaleController.isRTL ? 16 : 0, 0)); valueImageView = new ImageView(context); valueImageView.setScaleType(ImageView.ScaleType.CENTER); - addView(valueImageView); - layoutParams = (LayoutParams) valueImageView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 24 : 0); - layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 0 : 24); - layoutParams.gravity = (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL; - valueImageView.setLayoutParams(layoutParams); + addView(valueImageView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL, LocaleController.isRTL ? 24 : 0, 0, LocaleController.isRTL ? 0 : 24, 0)); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckCell.java index 4e5da0292..a36f01ff0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckCell.java @@ -19,6 +19,7 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; import org.telegram.ui.Components.FrameLayoutFixed; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.Switch; public class TextCheckCell extends FrameLayoutFixed { @@ -44,28 +45,14 @@ public class TextCheckCell extends FrameLayoutFixed { textView.setMaxLines(1); textView.setSingleLine(true); textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); - addView(textView); - LayoutParams layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; - layoutParams.leftMargin = AndroidUtilities.dp(17); - layoutParams.rightMargin = AndroidUtilities.dp(17); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - textView.setLayoutParams(layoutParams); + addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 17, 0, 17, 0)); checkBox = new Switch(context); checkBox.setDuplicateParentStateEnabled(false); checkBox.setFocusable(false); checkBox.setFocusableInTouchMode(false); checkBox.setClickable(false); - addView(checkBox); - layoutParams = (LayoutParams) checkBox.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.leftMargin = AndroidUtilities.dp(14); - layoutParams.rightMargin = AndroidUtilities.dp(14); - layoutParams.gravity = (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL; - checkBox.setLayoutParams(layoutParams); + addView(checkBox, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL, 14, 0, 14, 0)); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextColorCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextColorCell.java index 59b6c9e61..2fb0de5c6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextColorCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextColorCell.java @@ -22,14 +22,17 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; import org.telegram.messenger.R; +import org.telegram.ui.Components.LayoutHelper; public class TextColorCell extends FrameLayout { + private TextView textView; - private Drawable colorDrawable; - private static Paint paint; private boolean needDivider; private int currentColor; + private static Drawable colorDrawable; + private static Paint paint; + public TextColorCell(Context context) { super(context); @@ -37,6 +40,8 @@ public class TextColorCell extends FrameLayout { paint = new Paint(); paint.setColor(0xffd9d9d9); paint.setStrokeWidth(1); + + colorDrawable = getResources().getDrawable(R.drawable.switch_to_on2); } textView = new TextView(context); @@ -46,16 +51,7 @@ public class TextColorCell extends FrameLayout { textView.setMaxLines(1); textView.setSingleLine(true); textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); - addView(textView); - LayoutParams layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; - layoutParams.leftMargin = AndroidUtilities.dp(17); - layoutParams.rightMargin = AndroidUtilities.dp(17); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - textView.setLayoutParams(layoutParams); - - colorDrawable = getResources().getDrawable(R.drawable.switch_to_on2); + addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 17, 0, 17, 0)); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextDetailCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextDetailCell.java index 513daf871..7889b7803 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextDetailCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextDetailCell.java @@ -18,6 +18,7 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; +import org.telegram.ui.Components.LayoutHelper; public class TextDetailCell extends FrameLayout { @@ -35,15 +36,7 @@ public class TextDetailCell extends FrameLayout { textView.setMaxLines(1); textView.setSingleLine(true); textView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); - addView(textView); - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.topMargin = AndroidUtilities.dp(10); - layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : 71); - layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 71 : 16); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - textView.setLayoutParams(layoutParams); + addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT, LocaleController.isRTL ? 16 : 71, 10, LocaleController.isRTL ? 16 : 71, 0)); valueTextView = new TextView(context); valueTextView.setTextColor(0xff8a8a8a); @@ -52,26 +45,11 @@ public class TextDetailCell extends FrameLayout { valueTextView.setMaxLines(1); valueTextView.setSingleLine(true); valueTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); - addView(valueTextView); - layoutParams = (FrameLayout.LayoutParams) valueTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.topMargin = AndroidUtilities.dp(35); - layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : 71); - layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 71 : 16); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - valueTextView.setLayoutParams(layoutParams); + addView(valueTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT, LocaleController.isRTL ? 16 : 71, 35, LocaleController.isRTL ? 16 : 71, 0)); imageView = new ImageView(context); imageView.setScaleType(ImageView.ScaleType.CENTER); - addView(imageView); - layoutParams = (LayoutParams) imageView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 0 : 16); - layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : 0); - layoutParams.gravity = (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL; - imageView.setLayoutParams(layoutParams); + addView(imageView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL, LocaleController.isRTL ? 0 : 16, 0, LocaleController.isRTL ? 16 : 0, 0)); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextDetailSettingsCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextDetailSettingsCell.java index 4d62db658..1d9a9a062 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextDetailSettingsCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextDetailSettingsCell.java @@ -18,6 +18,7 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; import org.telegram.ui.Components.FrameLayoutFixed; +import org.telegram.ui.Components.LayoutHelper; public class TextDetailSettingsCell extends FrameLayoutFixed { @@ -43,15 +44,7 @@ public class TextDetailSettingsCell extends FrameLayoutFixed { textView.setMaxLines(1); textView.setSingleLine(true); textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); - addView(textView); - LayoutParams layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.topMargin = AndroidUtilities.dp(10); - layoutParams.leftMargin = AndroidUtilities.dp(17); - layoutParams.rightMargin = AndroidUtilities.dp(17); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - textView.setLayoutParams(layoutParams); + addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 17, 10, 17, 0)); valueTextView = new TextView(context); valueTextView.setTextColor(0xff8a8a8a); @@ -61,15 +54,7 @@ public class TextDetailSettingsCell extends FrameLayoutFixed { valueTextView.setMaxLines(1); valueTextView.setSingleLine(true); valueTextView.setPadding(0, 0, 0, 0); - addView(valueTextView); - layoutParams = (LayoutParams) valueTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.topMargin = AndroidUtilities.dp(35); - layoutParams.leftMargin = AndroidUtilities.dp(17); - layoutParams.rightMargin = AndroidUtilities.dp(17); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - valueTextView.setLayoutParams(layoutParams); + addView(valueTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 17, 35, 17, 0)); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextInfoCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextInfoCell.java index 5125a3c72..1e0f3ceec 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextInfoCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextInfoCell.java @@ -15,6 +15,7 @@ import android.widget.FrameLayout; import android.widget.TextView; import org.telegram.android.AndroidUtilities; +import org.telegram.ui.Components.LayoutHelper; public class TextInfoCell extends FrameLayout { @@ -28,14 +29,7 @@ public class TextInfoCell extends FrameLayout { textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); textView.setGravity(Gravity.CENTER); textView.setPadding(0, AndroidUtilities.dp(19), 0, AndroidUtilities.dp(19)); - addView(textView); - LayoutParams layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.leftMargin = AndroidUtilities.dp(17); - layoutParams.rightMargin = AndroidUtilities.dp(17); - layoutParams.gravity = Gravity.CENTER; - textView.setLayoutParams(layoutParams); + addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 17, 0, 17, 0)); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextInfoPrivacyCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextInfoPrivacyCell.java index 6c8bcffa6..579917310 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextInfoPrivacyCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextInfoPrivacyCell.java @@ -9,6 +9,7 @@ package org.telegram.ui.Cells; import android.content.Context; +import android.text.method.LinkMovementMethod; import android.util.TypedValue; import android.view.Gravity; import android.widget.FrameLayout; @@ -16,6 +17,7 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; +import org.telegram.ui.Components.LayoutHelper; public class TextInfoPrivacyCell extends FrameLayout { @@ -26,17 +28,12 @@ public class TextInfoPrivacyCell extends FrameLayout { textView = new TextView(context); textView.setTextColor(0xff808080); + textView.setLinkTextColor(0xff316f9f); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); textView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); textView.setPadding(0, AndroidUtilities.dp(10), 0, AndroidUtilities.dp(17)); - addView(textView); - LayoutParams layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.leftMargin = AndroidUtilities.dp(17); - layoutParams.rightMargin = AndroidUtilities.dp(17); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - textView.setLayoutParams(layoutParams); + textView.setMovementMethod(LinkMovementMethod.getInstance()); + addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 17, 0, 17, 0)); } @Override @@ -44,7 +41,7 @@ public class TextInfoPrivacyCell extends FrameLayout { super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); } - public void setText(String text) { + public void setText(CharSequence text) { textView.setText(text); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSettingsCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSettingsCell.java index e3c3ffb58..fdda610f4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSettingsCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSettingsCell.java @@ -20,6 +20,7 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; +import org.telegram.ui.Components.LayoutHelper; public class TextSettingsCell extends FrameLayout { @@ -46,14 +47,7 @@ public class TextSettingsCell extends FrameLayout { textView.setSingleLine(true); textView.setEllipsize(TextUtils.TruncateAt.END); textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); - addView(textView); - LayoutParams layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; - layoutParams.leftMargin = AndroidUtilities.dp(17); - layoutParams.rightMargin = AndroidUtilities.dp(17); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - textView.setLayoutParams(layoutParams); + addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 17, 0, 17, 0)); valueTextView = new TextView(context); valueTextView.setTextColor(0xff2f8cc9); @@ -63,26 +57,12 @@ public class TextSettingsCell extends FrameLayout { valueTextView.setSingleLine(true); valueTextView.setEllipsize(TextUtils.TruncateAt.END); valueTextView.setGravity((LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL); - addView(valueTextView); - layoutParams = (LayoutParams) valueTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.MATCH_PARENT; - layoutParams.leftMargin = AndroidUtilities.dp(17); - layoutParams.rightMargin = AndroidUtilities.dp(17); - layoutParams.gravity = LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT; - valueTextView.setLayoutParams(layoutParams); + addView(valueTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.TOP, 17, 0, 17, 0)); valueImageView = new ImageView(context); valueImageView.setScaleType(ImageView.ScaleType.CENTER); valueImageView.setVisibility(INVISIBLE); - addView(valueImageView); - layoutParams = (LayoutParams) valueImageView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 17 : 0); - layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 0 : 17); - layoutParams.gravity = (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL; - valueImageView.setLayoutParams(layoutParams); + addView(valueImageView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL, 17, 0, 17, 0)); } @Override @@ -126,6 +106,7 @@ public class TextSettingsCell extends FrameLayout { } needDivider = divider; setWillNotDraw(!divider); + requestLayout(); } public void setTextAndIcon(String text, int resId, boolean divider) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/UserCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/UserCell.java index 909be3b41..116721b01 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/UserCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/UserCell.java @@ -9,12 +9,9 @@ package org.telegram.ui.Cells; import android.content.Context; -import android.text.TextUtils; -import android.util.TypedValue; import android.view.Gravity; import android.widget.FrameLayout; import android.widget.ImageView; -import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.ContactsController; @@ -27,12 +24,14 @@ import org.telegram.messenger.UserConfig; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.CheckBox; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.SimpleTextView; public class UserCell extends FrameLayout { private BackupImageView avatarImageView; - private TextView nameTextView; - private TextView statusTextView; + private SimpleTextView nameTextView; + private SimpleTextView statusTextView; private ImageView imageView; private CheckBox checkBox; @@ -53,76 +52,31 @@ public class UserCell extends FrameLayout { public UserCell(Context context, int padding) { super(context); - avatarImageView = new BackupImageView(context); - avatarImageView.setRoundRadius(AndroidUtilities.dp(24)); - addView(avatarImageView); - LayoutParams layoutParams = (LayoutParams) avatarImageView.getLayoutParams(); - layoutParams.width = AndroidUtilities.dp(48); - layoutParams.height = AndroidUtilities.dp(48); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - layoutParams.leftMargin = LocaleController.isRTL ? 0 : AndroidUtilities.dp(7 + padding); - layoutParams.rightMargin = LocaleController.isRTL ? AndroidUtilities.dp(7 + padding) : 0; - layoutParams.topMargin = AndroidUtilities.dp(8); - avatarImageView.setLayoutParams(layoutParams); avatarDrawable = new AvatarDrawable(); - nameTextView = new TextView(context); - nameTextView.setTextColor(0xff212121); - nameTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 17); - nameTextView.setLines(1); - nameTextView.setMaxLines(1); - nameTextView.setSingleLine(true); - nameTextView.setEllipsize(TextUtils.TruncateAt.END); - nameTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); - addView(nameTextView); - layoutParams = (LayoutParams) nameTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : (68 + padding)); - layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? (68 + padding) : 16); - layoutParams.topMargin = AndroidUtilities.dp(10.5f); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - nameTextView.setLayoutParams(layoutParams); + avatarImageView = new BackupImageView(context); + avatarImageView.setRoundRadius(AndroidUtilities.dp(24)); + addView(avatarImageView, LayoutHelper.createFrame(48, 48, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 0 : 7 + padding, 8, LocaleController.isRTL ? 7 + padding : 0, 0)); - statusTextView = new TextView(context); - statusTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - statusTextView.setLines(1); - statusTextView.setMaxLines(1); - statusTextView.setSingleLine(true); - statusTextView.setEllipsize(TextUtils.TruncateAt.END); - statusTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); - addView(statusTextView); - layoutParams = (LayoutParams) statusTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : (68 + padding)); - layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? (68 + padding) : 16); - layoutParams.topMargin = AndroidUtilities.dp(33.5f); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - statusTextView.setLayoutParams(layoutParams); + nameTextView = new SimpleTextView(context); + nameTextView.setTextColor(0xff212121); + nameTextView.setTextSize(17); + nameTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); + addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 20, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 28 : (68 + padding), 11.5f, LocaleController.isRTL ? (68 + padding) : 28, 0)); + + statusTextView = new SimpleTextView(context); + statusTextView.setTextSize(14); + statusTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); + addView(statusTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 20, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 28 : (68 + padding), 34.5f, LocaleController.isRTL ? (68 + padding) : 28, 0)); imageView = new ImageView(context); imageView.setScaleType(ImageView.ScaleType.CENTER); - addView(imageView); - layoutParams = (LayoutParams) imageView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 0 : 16); - layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : 0); - layoutParams.gravity = (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL; - imageView.setLayoutParams(layoutParams); + imageView.setVisibility(GONE); + addView(imageView, LayoutHelper.createFrame(LayoutParams.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL, LocaleController.isRTL ? 0 : 16, 0, LocaleController.isRTL ? 16 : 0, 0)); checkBox = new CheckBox(context, R.drawable.round_check2); checkBox.setVisibility(INVISIBLE); - addView(checkBox); - layoutParams = (LayoutParams) checkBox.getLayoutParams(); - layoutParams.width = AndroidUtilities.dp(22); - layoutParams.height = AndroidUtilities.dp(22); - layoutParams.topMargin = AndroidUtilities.dp(38); - layoutParams.leftMargin = LocaleController.isRTL ? 0 : AndroidUtilities.dp(37 + padding); - layoutParams.rightMargin = LocaleController.isRTL ? AndroidUtilities.dp(37 + padding) : 0; - layoutParams.gravity = (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); - checkBox.setLayoutParams(layoutParams); + addView(checkBox, LayoutHelper.createFrame(22, 22, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 0 : 37 + padding, 38, LocaleController.isRTL ? 37 + padding : 0, 0)); } public void setData(TLRPC.User user, CharSequence name, CharSequence status, int resId) { @@ -149,12 +103,6 @@ public class UserCell extends FrameLayout { checkBox.setChecked(checked, animated); } - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - lastAvatar = null; - } - @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(64), MeasureSpec.EXACTLY)); @@ -170,6 +118,7 @@ public class UserCell extends FrameLayout { return; } TLRPC.FileLocation photo = null; + String newName = null; if (currentUser.photo != null) { photo = currentUser.photo.photo_small; } @@ -190,9 +139,9 @@ public class UserCell extends FrameLayout { continueUpdate = true; } } - if (!continueUpdate && (mask & MessagesController.UPDATE_MASK_NAME) != 0) { - String newName = currentUser.first_name + currentUser.last_name; - if (newName == null || !newName.equals(lastName)) { + if (!continueUpdate && currentName == null && lastName != null && (mask & MessagesController.UPDATE_MASK_NAME) != 0) { + newName = ContactsController.formatName(currentUser.first_name, currentUser.last_name); + if (!newName.equals(lastName)) { continueUpdate = true; } } @@ -207,29 +156,31 @@ public class UserCell extends FrameLayout { } else { lastStatus = 0; } - lastName = currentUser.first_name + currentUser.last_name; - lastAvatar = photo; if (currentName != null) { + lastName = null; nameTextView.setText(currentName); } else { - nameTextView.setText(ContactsController.formatName(currentUser.first_name, currentUser.last_name)); + lastName = newName == null ? ContactsController.formatName(currentUser.first_name, currentUser.last_name) : newName; + nameTextView.setText(lastName); } if (currrntStatus != null) { - statusTextView.setText(currrntStatus); statusTextView.setTextColor(statusColor); + statusTextView.setText(currrntStatus); } else { if (currentUser.id == UserConfig.getClientUserId() || currentUser.status != null && currentUser.status.expires > ConnectionsManager.getInstance().getCurrentTime()) { - statusTextView.setText(LocaleController.getString("Online", R.string.Online)); statusTextView.setTextColor(statusOnlineColor); + statusTextView.setText(LocaleController.getString("Online", R.string.Online)); } else { - statusTextView.setText(LocaleController.formatUserStatus(currentUser)); statusTextView.setTextColor(statusColor); + statusTextView.setText(LocaleController.formatUserStatus(currentUser)); } } - imageView.setVisibility(currentDrawable == 0 ? INVISIBLE : VISIBLE); - imageView.setImageResource(currentDrawable); + if (imageView.getVisibility() == VISIBLE && currentDrawable == 0 || imageView.getVisibility() == GONE && currentDrawable != 0) { + imageView.setVisibility(currentDrawable == 0 ? GONE : VISIBLE); + imageView.setImageResource(currentDrawable); + } avatarImageView.setImage(photo, "50_50", avatarDrawable); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/WallpaperCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/WallpaperCell.java new file mode 100644 index 000000000..5dce6aaf4 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/WallpaperCell.java @@ -0,0 +1,75 @@ +/* + * This is the source code of Telegram for Android v. 2.x.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2015. + */ + +package org.telegram.ui.Cells; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.view.Gravity; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import org.telegram.android.AndroidUtilities; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.R; +import org.telegram.messenger.TLRPC; +import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.LayoutHelper; + +public class WallpaperCell extends FrameLayout { + + private BackupImageView imageView; + private View selectionView; + private ImageView imageView2; + + public WallpaperCell(Context context) { + super(context); + + imageView = new BackupImageView(context); + addView(imageView, LayoutHelper.createFrame(100, 100, Gravity.LEFT | Gravity.BOTTOM)); + + imageView2 = new ImageView(context); + imageView2.setImageResource(R.drawable.ic_gallery_background); + imageView2.setScaleType(ImageView.ScaleType.CENTER); + addView(imageView2, LayoutHelper.createFrame(100, 100, Gravity.LEFT | Gravity.BOTTOM)); + + selectionView = new View(context); + selectionView.setBackgroundResource(R.drawable.wall_selection); + addView(selectionView, LayoutHelper.createFrame(100, 102)); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(100), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(102), MeasureSpec.EXACTLY)); + } + + public void setWallpaper(TLRPC.WallPaper wallpaper, int selectedBackground) { + if (wallpaper == null) { + imageView.setVisibility(INVISIBLE); + imageView2.setVisibility(VISIBLE); + selectionView.setVisibility(selectedBackground == -1 ? View.VISIBLE : INVISIBLE); + imageView2.setBackgroundColor(selectedBackground == -1 || selectedBackground == 1000001 ? 0x5a475866 : 0x5a000000); + } else { + imageView.setVisibility(VISIBLE); + imageView2.setVisibility(INVISIBLE); + selectionView.setVisibility(selectedBackground == wallpaper.id ? View.VISIBLE : INVISIBLE); + + if (wallpaper instanceof TLRPC.TL_wallPaperSolid) { + imageView.setImageBitmap(null); + imageView.setBackgroundColor(0xff000000 | wallpaper.bg_color); + } else { + TLRPC.PhotoSize size = FileLoader.getClosestPhotoSizeWithSize(wallpaper.sizes, AndroidUtilities.dp(100)); + if (size != null && size.location != null) { + imageView.setImage(size.location, "100_100", (Drawable) null); + } + imageView.setBackgroundColor(0x5a475866); + } + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChangeChatNameActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChangeChatNameActivity.java index eb94acb69..8eb72c7e1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChangeChatNameActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChangeChatNameActivity.java @@ -34,6 +34,7 @@ import org.telegram.messenger.R; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; public class ChangeChatNameActivity extends BaseFragment { @@ -79,7 +80,8 @@ public class ChangeChatNameActivity extends BaseFragment { TLRPC.Chat currentChat = MessagesController.getInstance().getChat(chat_id); - fragmentView = new LinearLayout(context); + LinearLayout linearLayout = new LinearLayout(context); + fragmentView = linearLayout; fragmentView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); ((LinearLayout) fragmentView).setOrientation(LinearLayout.VERTICAL); fragmentView.setOnTouchListener(new View.OnTouchListener() { @@ -112,14 +114,7 @@ public class ChangeChatNameActivity extends BaseFragment { } }); - ((LinearLayout) fragmentView).addView(firstNameField); - LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) firstNameField.getLayoutParams(); - layoutParams.topMargin = AndroidUtilities.dp(24); - layoutParams.height = AndroidUtilities.dp(36); - layoutParams.leftMargin = AndroidUtilities.dp(24); - layoutParams.rightMargin = AndroidUtilities.dp(24); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; - firstNameField.setLayoutParams(layoutParams); + linearLayout.addView(firstNameField, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 36, 24, 24, 24, 0)); if (chat_id > 0) { firstNameField.setHint(LocaleController.getString("GroupName", R.string.GroupName)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChangeNameActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChangeNameActivity.java index 4c672106e..5c0088490 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChangeNameActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChangeNameActivity.java @@ -38,6 +38,7 @@ import org.telegram.messenger.UserConfig; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; public class ChangeNameActivity extends BaseFragment { @@ -75,7 +76,8 @@ public class ChangeNameActivity extends BaseFragment { user = UserConfig.getCurrentUser(); } - fragmentView = new LinearLayout(context); + LinearLayout linearLayout = new LinearLayout(context); + fragmentView = linearLayout; fragmentView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); ((LinearLayout) fragmentView).setOrientation(LinearLayout.VERTICAL); fragmentView.setOnTouchListener(new View.OnTouchListener() { @@ -97,14 +99,7 @@ public class ChangeNameActivity extends BaseFragment { firstNameField.setImeOptions(EditorInfo.IME_ACTION_NEXT); firstNameField.setHint(LocaleController.getString("FirstName", R.string.FirstName)); AndroidUtilities.clearCursorDrawable(firstNameField); - ((LinearLayout) fragmentView).addView(firstNameField); - LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) firstNameField.getLayoutParams(); - layoutParams.topMargin = AndroidUtilities.dp(24); - layoutParams.height = AndroidUtilities.dp(36); - layoutParams.leftMargin = AndroidUtilities.dp(24); - layoutParams.rightMargin = AndroidUtilities.dp(24); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; - firstNameField.setLayoutParams(layoutParams); + linearLayout.addView(firstNameField, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 36, 24, 24, 24, 0)); firstNameField.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) { @@ -129,14 +124,7 @@ public class ChangeNameActivity extends BaseFragment { lastNameField.setImeOptions(EditorInfo.IME_ACTION_DONE); lastNameField.setHint(LocaleController.getString("LastName", R.string.LastName)); AndroidUtilities.clearCursorDrawable(lastNameField); - ((LinearLayout) fragmentView).addView(lastNameField); - layoutParams = (LinearLayout.LayoutParams) lastNameField.getLayoutParams(); - layoutParams.topMargin = AndroidUtilities.dp(16); - layoutParams.height = AndroidUtilities.dp(36); - layoutParams.leftMargin = AndroidUtilities.dp(24); - layoutParams.rightMargin = AndroidUtilities.dp(24); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; - lastNameField.setLayoutParams(layoutParams); + linearLayout.addView(lastNameField, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 36, 24, 16, 24, 0)); lastNameField.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChangePhoneActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChangePhoneActivity.java index 01e54b550..1887a03b0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChangePhoneActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChangePhoneActivity.java @@ -8,6 +8,7 @@ package org.telegram.ui; +import android.annotation.SuppressLint; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Context; @@ -53,10 +54,11 @@ import org.telegram.messenger.UserConfig; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.BaseFragment; -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 org.telegram.android.AnimationCompat.AnimatorListenerAdapterProxy; +import org.telegram.android.AnimationCompat.AnimatorSetProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.ViewProxy; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.SlideView; import org.telegram.ui.Components.TypefaceSpan; @@ -131,27 +133,11 @@ public class ChangePhoneActivity extends BaseFragment { views[0] = new PhoneView(context); views[0].setVisibility(View.VISIBLE); - frameLayout.addView(views[0]); - FrameLayout.LayoutParams layoutParams1 = (FrameLayout.LayoutParams) views[0].getLayoutParams(); - layoutParams1.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams1.leftMargin = AndroidUtilities.dp(16); - layoutParams1.rightMargin = AndroidUtilities.dp(16); - layoutParams1.topMargin = AndroidUtilities.dp(30); - layoutParams1.gravity = Gravity.TOP | Gravity.LEFT; - views[0].setLayoutParams(layoutParams1); + frameLayout.addView(views[0], LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.LEFT, 16, 30, 16, 0)); views[1] = new LoginActivitySmsView(context); views[1].setVisibility(View.GONE); - frameLayout.addView(views[1]); - layoutParams1 = (FrameLayout.LayoutParams) views[1].getLayoutParams(); - layoutParams1.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams1.leftMargin = AndroidUtilities.dp(16); - layoutParams1.rightMargin = AndroidUtilities.dp(16); - layoutParams1.topMargin = AndroidUtilities.dp(30); - layoutParams1.gravity = Gravity.TOP | Gravity.LEFT; - views[1].setLayoutParams(layoutParams1); + frameLayout.addView(views[1], LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT, 16, 30, 16, 0)); try { if (views[0] == null || views[1] == null) { @@ -208,7 +194,7 @@ public class ChangePhoneActivity extends BaseFragment { builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); builder.setMessage(text); builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null); - showAlertDialog(builder); + showDialog(builder.create()); } public void needShowProgress() { @@ -257,6 +243,7 @@ public class ChangePhoneActivity extends BaseFragment { newView.setVisibility(View.VISIBLE); } + @SuppressLint("NewApi") @Override public void onAnimationEnd(Object animation) { outView.setVisibility(View.GONE); @@ -307,7 +294,7 @@ public class ChangePhoneActivity extends BaseFragment { countryButton.setBackgroundResource(R.drawable.spinner_states); addView(countryButton); LayoutParams layoutParams = (LayoutParams) countryButton.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(36); layoutParams.leftMargin = AndroidUtilities.dp(20); layoutParams.rightMargin = AndroidUtilities.dp(20); @@ -333,7 +320,7 @@ public class ChangePhoneActivity extends BaseFragment { view.setBackgroundColor(0xffdbdbdb); addView(view); layoutParams = (LayoutParams) view.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = 1; layoutParams.leftMargin = AndroidUtilities.dp(24); layoutParams.rightMargin = AndroidUtilities.dp(24); @@ -344,8 +331,8 @@ public class ChangePhoneActivity extends BaseFragment { linearLayout.setOrientation(HORIZONTAL); addView(linearLayout); layoutParams = (LayoutParams) linearLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.topMargin = AndroidUtilities.dp(20); linearLayout.setLayoutParams(layoutParams); @@ -355,8 +342,8 @@ public class ChangePhoneActivity extends BaseFragment { textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); linearLayout.addView(textView); layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(24); textView.setLayoutParams(layoutParams); @@ -447,7 +434,7 @@ public class ChangePhoneActivity extends BaseFragment { phoneField.setImeOptions(EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NO_EXTRACT_UI); linearLayout.addView(phoneField); layoutParams = (LayoutParams) phoneField.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(36); layoutParams.rightMargin = AndroidUtilities.dp(24); phoneField.setLayoutParams(layoutParams); @@ -517,8 +504,8 @@ public class ChangePhoneActivity extends BaseFragment { textView.setLineSpacing(AndroidUtilities.dp(2), 1.0f); addView(textView); layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(24); layoutParams.rightMargin = AndroidUtilities.dp(24); layoutParams.topMargin = AndroidUtilities.dp(28); @@ -747,8 +734,8 @@ public class ChangePhoneActivity extends BaseFragment { confirmTextView.setLineSpacing(AndroidUtilities.dp(2), 1.0f); addView(confirmTextView); LayoutParams layoutParams = (LayoutParams) confirmTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.LEFT; layoutParams.leftMargin = AndroidUtilities.dp(24); layoutParams.rightMargin = AndroidUtilities.dp(24); @@ -766,7 +753,7 @@ public class ChangePhoneActivity extends BaseFragment { codeField.setPadding(0, 0, 0, 0); addView(codeField); layoutParams = (LayoutParams) codeField.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(36); layoutParams.gravity = Gravity.CENTER_HORIZONTAL; layoutParams.topMargin = AndroidUtilities.dp(20); @@ -791,8 +778,8 @@ public class ChangePhoneActivity extends BaseFragment { timeText.setGravity(Gravity.LEFT); addView(timeText); layoutParams = (LayoutParams) timeText.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.LEFT; layoutParams.topMargin = AndroidUtilities.dp(30); layoutParams.leftMargin = AndroidUtilities.dp(24); @@ -803,8 +790,8 @@ public class ChangePhoneActivity extends BaseFragment { linearLayout.setGravity(Gravity.BOTTOM | Gravity.CENTER_VERTICAL); addView(linearLayout); layoutParams = (LayoutParams) linearLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; linearLayout.setLayoutParams(layoutParams); TextView wrongNumber = new TextView(context); @@ -815,8 +802,8 @@ public class ChangePhoneActivity extends BaseFragment { wrongNumber.setPadding(0, AndroidUtilities.dp(24), 0, 0); linearLayout.addView(wrongNumber); layoutParams = (LayoutParams) wrongNumber.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.BOTTOM | Gravity.LEFT; layoutParams.bottomMargin = AndroidUtilities.dp(10); layoutParams.leftMargin = AndroidUtilities.dp(24); @@ -1065,18 +1052,13 @@ public class ChangePhoneActivity extends BaseFragment { @Override public void didReceivedNotification(int id, final Object... args) { if (id == NotificationCenter.didReceiveSmsCode) { - AndroidUtilities.runOnUIThread(new Runnable() { - @Override - public void run() { - if (!waitingForSms) { - return; - } - if (codeField != null) { - codeField.setText("" + args[0]); - onNextPressed(); - } - } - }); + if (!waitingForSms) { + return; + } + if (codeField != null) { + codeField.setText("" + args[0]); + onNextPressed(); + } } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChangePhoneHelpActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChangePhoneHelpActivity.java index 3da860185..e4077ef3b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChangePhoneHelpActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChangePhoneHelpActivity.java @@ -31,6 +31,7 @@ import org.telegram.messenger.TLRPC; import org.telegram.messenger.UserConfig; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; public class ChangePhoneHelpActivity extends BaseFragment { @@ -70,8 +71,8 @@ public class ChangePhoneHelpActivity extends BaseFragment { ScrollView scrollView = new ScrollView(context); relativeLayout.addView(scrollView); RelativeLayout.LayoutParams layoutParams3 = (RelativeLayout.LayoutParams) scrollView.getLayoutParams(); - layoutParams3.width = RelativeLayout.LayoutParams.MATCH_PARENT; - layoutParams3.height = RelativeLayout.LayoutParams.WRAP_CONTENT; + layoutParams3.width = LayoutHelper.MATCH_PARENT; + layoutParams3.height = LayoutHelper.WRAP_CONTENT; layoutParams3.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE); scrollView.setLayoutParams(layoutParams3); @@ -88,8 +89,8 @@ public class ChangePhoneHelpActivity extends BaseFragment { imageView.setImageResource(R.drawable.phone_change); linearLayout.addView(imageView); LinearLayout.LayoutParams layoutParams2 = (LinearLayout.LayoutParams) imageView.getLayoutParams(); - layoutParams2.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = LinearLayout.LayoutParams.WRAP_CONTENT; + layoutParams2.width = LayoutHelper.WRAP_CONTENT; + layoutParams2.height = LayoutHelper.WRAP_CONTENT; layoutParams2.gravity = Gravity.CENTER_HORIZONTAL; imageView.setLayoutParams(layoutParams2); @@ -106,8 +107,8 @@ public class ChangePhoneHelpActivity extends BaseFragment { } linearLayout.addView(textView); layoutParams2 = (LinearLayout.LayoutParams) textView.getLayoutParams(); - layoutParams2.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = LinearLayout.LayoutParams.WRAP_CONTENT; + layoutParams2.width = LayoutHelper.WRAP_CONTENT; + layoutParams2.height = LayoutHelper.WRAP_CONTENT; layoutParams2.gravity = Gravity.CENTER_HORIZONTAL; layoutParams2.leftMargin = AndroidUtilities.dp(20); layoutParams2.rightMargin = AndroidUtilities.dp(20); @@ -123,8 +124,8 @@ public class ChangePhoneHelpActivity extends BaseFragment { textView.setPadding(0, AndroidUtilities.dp(10), 0, AndroidUtilities.dp(10)); linearLayout.addView(textView); layoutParams2 = (LinearLayout.LayoutParams) textView.getLayoutParams(); - layoutParams2.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = LinearLayout.LayoutParams.WRAP_CONTENT; + layoutParams2.width = LayoutHelper.WRAP_CONTENT; + layoutParams2.height = LayoutHelper.WRAP_CONTENT; layoutParams2.gravity = Gravity.CENTER_HORIZONTAL; layoutParams2.leftMargin = AndroidUtilities.dp(20); layoutParams2.rightMargin = AndroidUtilities.dp(20); @@ -147,7 +148,7 @@ public class ChangePhoneHelpActivity extends BaseFragment { } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } }); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChangeUsernameActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChangeUsernameActivity.java index e0169de83..0e40b1a41 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChangeUsernameActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChangeUsernameActivity.java @@ -15,7 +15,6 @@ import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; import android.text.Editable; -import android.text.Html; import android.text.InputType; import android.text.TextWatcher; import android.util.TypedValue; @@ -46,6 +45,7 @@ import org.telegram.messenger.UserConfig; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; import java.util.ArrayList; @@ -125,7 +125,7 @@ public class ChangeUsernameActivity extends BaseFragment { layoutParams.height = AndroidUtilities.dp(36); layoutParams.leftMargin = AndroidUtilities.dp(24); layoutParams.rightMargin = AndroidUtilities.dp(24); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; firstNameField.setLayoutParams(layoutParams); if (user != null && user.username != null && user.username.length() > 0) { @@ -139,8 +139,8 @@ public class ChangeUsernameActivity extends BaseFragment { ((LinearLayout) fragmentView).addView(checkTextView); layoutParams = (LinearLayout.LayoutParams) checkTextView.getLayoutParams(); layoutParams.topMargin = AndroidUtilities.dp(12); - layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; layoutParams.leftMargin = AndroidUtilities.dp(24); layoutParams.rightMargin = AndroidUtilities.dp(24); @@ -154,8 +154,8 @@ public class ChangeUsernameActivity extends BaseFragment { ((LinearLayout) fragmentView).addView(helpTextView); layoutParams = (LinearLayout.LayoutParams) helpTextView.getLayoutParams(); layoutParams.topMargin = AndroidUtilities.dp(10); - layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; layoutParams.leftMargin = AndroidUtilities.dp(24); layoutParams.rightMargin = AndroidUtilities.dp(24); @@ -215,7 +215,7 @@ public class ChangeUsernameActivity extends BaseFragment { break; } builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null); - showAlertDialog(builder); + showDialog(builder.create()); } private boolean checkUserName(final String name, boolean alert) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java index 801c41d59..133143686 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java @@ -10,7 +10,6 @@ package org.telegram.ui; import android.app.Activity; import android.app.AlertDialog; -import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -26,7 +25,6 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.provider.MediaStore; -import android.support.v7.widget.LinearLayoutManager; import android.text.TextUtils; import android.util.Base64; import android.util.SparseArray; @@ -39,14 +37,12 @@ import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.WindowManager; import android.webkit.MimeTypeMap; -import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.ProgressBar; -import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; @@ -59,12 +55,15 @@ import org.telegram.android.MessagesStorage; import org.telegram.android.NotificationsController; import org.telegram.android.SecretChatHelper; import org.telegram.android.SendMessagesHelper; +import org.telegram.android.VideoEditedInfo; import org.telegram.android.query.ReplyMessageQuery; +import org.telegram.android.query.StickersQuery; +import org.telegram.android.support.widget.LinearLayoutManager; +import org.telegram.android.support.widget.RecyclerView; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.FileLoader; import org.telegram.messenger.RPCRequest; import org.telegram.messenger.SerializedData; -import org.telegram.messenger.TLClassStore; import org.telegram.messenger.TLObject; import org.telegram.messenger.TLRPC; import org.telegram.android.ContactsController; @@ -75,37 +74,39 @@ import org.telegram.android.MessagesController; import org.telegram.android.NotificationCenter; import org.telegram.messenger.R; import org.telegram.messenger.UserConfig; -import org.telegram.messenger.Utilities; -import org.telegram.ui.Adapters.BaseFragmentAdapter; +import org.telegram.ui.ActionBar.BottomSheet; import org.telegram.ui.Adapters.MentionsAdapter; import org.telegram.ui.Adapters.StickersAdapter; -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 org.telegram.android.AnimationCompat.AnimatorListenerAdapterProxy; +import org.telegram.android.AnimationCompat.AnimatorSetProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.ViewProxy; import org.telegram.ui.Cells.ChatActionCell; import org.telegram.ui.Cells.ChatAudioCell; import org.telegram.ui.Cells.ChatBaseCell; import org.telegram.ui.Cells.ChatContactCell; import org.telegram.ui.Cells.ChatMediaCell; -import org.telegram.ui.Cells.ChatMessageCell; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; +import org.telegram.ui.Cells.ChatMessageCell; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.Components.ChatActivityEnterView; import org.telegram.android.ImageReceiver; import org.telegram.ui.Components.FrameLayoutFixed; -import org.telegram.ui.Components.LayoutListView; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.RecordStatusDrawable; import org.telegram.ui.Components.RecyclerListView; -import org.telegram.ui.Components.SizeNotifierRelativeLayout; +import org.telegram.ui.Components.ResourceLoader; +import org.telegram.ui.Components.SendingFileExDrawable; +import org.telegram.ui.Components.SizeNotifierFrameLayout; import org.telegram.ui.Components.TimerDrawable; import org.telegram.ui.Components.TypingDotsDrawable; +import org.telegram.ui.Components.WebFrameLayout; import java.io.File; -import java.io.RandomAccessFile; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -114,14 +115,16 @@ import java.util.concurrent.Semaphore; public class ChatActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate, MessagesActivity.MessagesActivityDelegate, PhotoViewer.PhotoViewerProvider { - private TLRPC.Chat currentChat; - private TLRPC.User currentUser; - private TLRPC.EncryptedChat currentEncryptedChat; + protected TLRPC.Chat currentChat; + protected TLRPC.User currentUser; + protected TLRPC.EncryptedChat currentEncryptedChat; private boolean userBlocked = false; + private ArrayList chatMessageCellsCache = new ArrayList<>(); + private ArrayList chatMediaCellsCache = new ArrayList<>(); + private FrameLayout progressView; private FrameLayout bottomOverlay; - private ChatAdapter chatAdapter; private ChatActivityEnterView chatActivityEnterView; private ImageView timeItem; private View timeItem2; @@ -130,11 +133,15 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private ActionBarMenuItem attachItem; private ActionBarMenuItem headerItem; private TextView addContactItem; - private LayoutListView chatListView; + private RecyclerListView chatListView; + private LinearLayoutManager chatLayoutManager; + private ChatActivityAdapter chatAdapter; private BackupImageView avatarImageView; private TextView bottomOverlayChatText; private FrameLayout bottomOverlayChat; private TypingDotsDrawable typingDotsDrawable; + private RecordStatusDrawable recordStatusDrawable; + private SendingFileExDrawable sendingFileDrawable; private FrameLayout emptyViewContainer; private ArrayList actionModeViews = new ArrayList<>(); private TextView nameTextView; @@ -164,6 +171,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private MessageObject forwaringMessage; private MessageObject replyingMessageObject; private boolean paused = true; + private boolean wasPaused = false; private boolean readWhenResume = false; private TLRPC.FileLocation replyImageLocation; private long linkSearchRequestId; @@ -185,7 +193,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private HashMap messagesDict = new HashMap<>(); private HashMap> messagesByDays = new HashMap<>(); - private ArrayList messages = new ArrayList<>(); + protected ArrayList messages = new ArrayList<>(); private int maxMessageId = Integer.MAX_VALUE; private int minMessageId = Integer.MIN_VALUE; private int maxDate = Integer.MIN_VALUE; @@ -214,11 +222,14 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private String currentPicturePath; - private TLRPC.ChatParticipants info = null; + private Rect scrollRect = new Rect(); + + protected TLRPC.ChatParticipants info = null; private int onlineCount = -1; private CharSequence lastPrintString; private String lastStatus; + private int lastStatusDrawable; private long chatEnterTime = 0; private long chatLeaveTime = 0; @@ -247,19 +258,18 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private final static int id_chat_compose_panel = 1000; - AdapterView.OnItemLongClickListener onItemLongClickListener = new AdapterView.OnItemLongClickListener() { + RecyclerListView.OnItemLongClickListener onItemLongClickListener = new RecyclerListView.OnItemLongClickListener() { @Override - public boolean onItemLongClick(AdapterView adapter, View view, int position, long id) { + public void onItemClick(View view, int position) { if (!actionBar.isActionModeShowed()) { createMenu(view, false); } - return true; } }; - AdapterView.OnItemClickListener onItemClickListener = new AdapterView.OnItemClickListener() { + RecyclerListView.OnItemClickListener onItemClickListener = new RecyclerListView.OnItemClickListener() { @Override - public void onItemClick(AdapterView adapterView, View view, int i, long l) { + public void onItemClick(View view, int position) { if (actionBar.isActionModeShowed()) { processRowSelect(view); return; @@ -407,7 +417,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not NotificationCenter.getInstance().addObserver(this, NotificationCenter.chatInfoDidLoaded); NotificationCenter.getInstance().addObserver(this, NotificationCenter.contactsDidLoaded); NotificationCenter.getInstance().addObserver(this, NotificationCenter.encryptedChatUpdated); - NotificationCenter.getInstance().addObserver(this, NotificationCenter.messagesReadedEncrypted); + NotificationCenter.getInstance().addObserver(this, NotificationCenter.messagesReadEncrypted); NotificationCenter.getInstance().addObserver(this, NotificationCenter.removeAllMessagesFromDialog); NotificationCenter.getInstance().addObserver(this, NotificationCenter.audioProgressDidChanged); NotificationCenter.getInstance().addObserver(this, NotificationCenter.audioDidReset); @@ -422,6 +432,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not NotificationCenter.getInstance().addObserver(this, NotificationCenter.didLoadedReplyMessages); NotificationCenter.getInstance().addObserver(this, NotificationCenter.didReceivedWebpages); NotificationCenter.getInstance().addObserver(this, NotificationCenter.didReceivedWebpagesInUpdates); + NotificationCenter.getInstance().addObserver(this, NotificationCenter.messagesReadContent); super.onFragmentCreate(); @@ -444,6 +455,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not typingDotsDrawable = new TypingDotsDrawable(); typingDotsDrawable.setIsChat(currentChat != null); + recordStatusDrawable = new RecordStatusDrawable(); + recordStatusDrawable.setIsChat(currentChat != null); + sendingFileDrawable = new SendingFileExDrawable(); + sendingFileDrawable.setIsChat(currentChat != null); if (currentEncryptedChat != null && AndroidUtilities.getMyLayerVersion(currentEncryptedChat.layer) != SecretChatHelper.CURRENT_SECRET_CHAT_LAYER) { SecretChatHelper.getInstance().sendNotifyLayerMessage(currentEncryptedChat, null); @@ -470,7 +485,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not NotificationCenter.getInstance().removeObserver(this, NotificationCenter.messageSendError); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.chatInfoDidLoaded); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.encryptedChatUpdated); - NotificationCenter.getInstance().removeObserver(this, NotificationCenter.messagesReadedEncrypted); + NotificationCenter.getInstance().removeObserver(this, NotificationCenter.messagesReadEncrypted); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.removeAllMessagesFromDialog); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.contactsDidLoaded); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.audioProgressDidChanged); @@ -486,6 +501,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not NotificationCenter.getInstance().removeObserver(this, NotificationCenter.didLoadedReplyMessages); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.didReceivedWebpages); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.didReceivedWebpagesInUpdates); + NotificationCenter.getInstance().removeObserver(this, NotificationCenter.messagesReadContent); if (AndroidUtilities.isTablet()) { NotificationCenter.getInstance().postNotificationName(NotificationCenter.openedChatChanged, dialog_id, true); @@ -509,16 +525,25 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not @Override public View createView(Context context, LayoutInflater inflater) { + for (int a = 0; a < 8; a++) { + chatMessageCellsCache.add(new ChatMessageCell(context)); + } + for (int a = 0; a < 4; a++) { + chatMediaCellsCache.add(new ChatMediaCell(context)); + } + lastPrintString = null; lastStatus = null; hasOwnBackground = true; + ResourceLoader.loadRecources(context); + actionBar.setBackButtonImage(R.drawable.ic_ab_back); actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @Override public void onItemClick(final int id) { if (id == attach_photo || id == attach_gallery || id == attach_document || id == attach_video) { - String action = null; + String action; if (currentChat != null) { if (currentChat.participants_count > MessagesController.getInstance().groupBigSize) { if (id == attach_photo || id == attach_gallery) { @@ -545,6 +570,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } if (id == -1) { + if (chatActivityEnterView != null) { + chatActivityEnterView.hideEmojiPopup(); + } finishFragment(); } else if (id == -2) { selectedMessagesIds.clear(); @@ -554,7 +582,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } else if (id == attach_photo) { try { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - File image = Utilities.generatePicturePath(); + File image = AndroidUtilities.generatePicturePath(); if (image != null) { takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(image)); currentPicturePath = image.getAbsolutePath(); @@ -564,11 +592,11 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not FileLog.e("tmessages", e); } } else if (id == attach_gallery) { - PhotoAlbumPickerActivity fragment = new PhotoAlbumPickerActivity(false); + PhotoAlbumPickerActivity fragment = new PhotoAlbumPickerActivity(false, ChatActivity.this); fragment.setDelegate(new PhotoAlbumPickerActivity.PhotoAlbumPickerActivityDelegate() { @Override - public void didSelectPhotos(ArrayList photos, ArrayList webPhotos) { - SendMessagesHelper.prepareSendingPhotos(photos, null, dialog_id, replyingMessageObject); + public void didSelectPhotos(ArrayList photos, ArrayList captions, ArrayList webPhotos) { + SendMessagesHelper.prepareSendingPhotos(photos, null, dialog_id, replyingMessageObject, captions); SendMessagesHelper.prepareSendingPhotosSearch(webPhotos, dialog_id, replyingMessageObject); showReplyPanel(false, null, null, null, false, true); } @@ -576,23 +604,38 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not @Override public void startPhotoSelectActivity() { try { + Intent videoPickerIntent = new Intent(); + videoPickerIntent.setType("video/*"); + videoPickerIntent.setAction(Intent.ACTION_GET_CONTENT); + videoPickerIntent.putExtra(MediaStore.EXTRA_SIZE_LIMIT, (long) (1024 * 1024 * 1536)); + Intent photoPickerIntent = new Intent(Intent.ACTION_PICK); photoPickerIntent.setType("image/*"); - startActivityForResult(photoPickerIntent, 1); + Intent chooserIntent = Intent.createChooser(photoPickerIntent, null); + chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[]{videoPickerIntent}); + + startActivityForResult(chooserIntent, 1); } catch (Exception e) { FileLog.e("tmessages", e); } } + + @Override + public boolean didSelectVideo(String path) { + if (Build.VERSION.SDK_INT >= 16) { + return !openVideoEditor(path, true, true); + } else { + SendMessagesHelper.prepareSendingVideo(path, 0, 0, 0, 0, null, dialog_id, replyingMessageObject); + showReplyPanel(false, null, null, null, false, true); + return true; + } + } }); presentFragment(fragment); } else if (id == attach_video) { try { - Intent pickIntent = new Intent(); - pickIntent.setType("video/*"); - pickIntent.setAction(Intent.ACTION_GET_CONTENT); - pickIntent.putExtra(MediaStore.EXTRA_SIZE_LIMIT, (long) (1024 * 1024 * 1536)); Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); - File video = Utilities.generateVideoPath(); + File video = AndroidUtilities.generateVideoPath(); if (video != null) { if (Build.VERSION.SDK_INT >= 18) { takeVideoIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(video)); @@ -600,10 +643,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not takeVideoIntent.putExtra(MediaStore.EXTRA_SIZE_LIMIT, (long) (1024 * 1024 * 1536)); currentPicturePath = video.getAbsolutePath(); } - Intent chooserIntent = Intent.createChooser(pickIntent, null); - chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[]{takeVideoIntent}); - - startActivityForResult(chooserIntent, 2); + startActivityForResult(takeVideoIntent, 2); } catch (Exception e) { FileLog.e("tmessages", e); } @@ -614,8 +654,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not LocationActivity fragment = new LocationActivity(); fragment.setDelegate(new LocationActivity.LocationActivityDelegate() { @Override - public void didSelectLocation(double latitude, double longitude) { - SendMessagesHelper.getInstance().sendMessage(latitude, longitude, dialog_id, replyingMessageObject); + public void didSelectLocation(TLRPC.MessageMedia location) { + SendMessagesHelper.getInstance().sendMessage(location, dialog_id, replyingMessageObject); moveScrollToLastMessage(); showReplyPanel(false, null, null, null, false, true); if (paused) { @@ -705,7 +745,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } else if (id == forward) { Bundle args = new Bundle(); args.putBoolean("onlySelect", true); @@ -717,7 +757,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (getParentActivity() == null) { return; } - showAlertDialog(AndroidUtilities.buildTTLAlert(getParentActivity(), currentEncryptedChat)); + showDialog(AndroidUtilities.buildTTLAlert(getParentActivity(), currentEncryptedChat).create()); } else if (id == clear_history || id == delete_chat) { if (getParentActivity() == null) { return; @@ -739,7 +779,11 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not public void onClick(DialogInterface dialogInterface, int i) { if (id != clear_history) { if (isChat) { - MessagesController.getInstance().deleteUserFromChat((int) -dialog_id, MessagesController.getInstance().getUser(UserConfig.getClientUserId()), null); + if (currentChat.left || currentChat instanceof TLRPC.TL_chatForbidden) { + MessagesController.getInstance().deleteDialog(dialog_id, 0, false); + } else { + MessagesController.getInstance().deleteUserFromChat((int) -dialog_id, MessagesController.getInstance().getUser(UserConfig.getClientUserId()), null); + } } else { MessagesController.getInstance().deleteDialog(dialog_id, 0, false); } @@ -750,7 +794,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } else if (id == share_contact) { if (currentUser == null || getParentActivity() == null) { return; @@ -773,7 +817,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } } else if (id == mute) { boolean muted = MessagesController.getInstance().isDialogMuted(dialog_id); @@ -802,7 +846,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); SharedPreferences.Editor editor = preferences.edit(); - long flags = 0; + long flags; if (i == 3) { editor.putInt("notify2_" + dialog_id, 2); flags = 1; @@ -823,7 +867,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } ); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } else { SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); SharedPreferences.Editor editor = preferences.edit(); @@ -855,14 +899,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not avatarContainer = new FrameLayoutFixed(context); avatarContainer.setBackgroundResource(R.drawable.bar_selector); avatarContainer.setPadding(AndroidUtilities.dp(8), 0, AndroidUtilities.dp(8), 0); - actionBar.addView(avatarContainer); - FrameLayout.LayoutParams layoutParams2 = (FrameLayout.LayoutParams) avatarContainer.getLayoutParams(); - layoutParams2.height = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.rightMargin = AndroidUtilities.dp(40); - layoutParams2.leftMargin = AndroidUtilities.dp(56); - layoutParams2.gravity = Gravity.TOP | Gravity.LEFT; - avatarContainer.setLayoutParams(layoutParams2); + actionBar.addView(avatarContainer, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT, 56, 0, 40, 0)); avatarContainer.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -895,37 +932,21 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not avatarImageView = new BackupImageView(context); avatarImageView.setRoundRadius(AndroidUtilities.dp(21)); - avatarContainer.addView(avatarImageView); - layoutParams2 = (FrameLayout.LayoutParams) avatarImageView.getLayoutParams(); - layoutParams2.width = AndroidUtilities.dp(42); - layoutParams2.height = AndroidUtilities.dp(42); - layoutParams2.topMargin = AndroidUtilities.dp(3); - layoutParams2.gravity = Gravity.TOP | Gravity.LEFT; - avatarImageView.setLayoutParams(layoutParams2); + avatarContainer.addView(avatarImageView, LayoutHelper.createFrame(42, 42, Gravity.TOP | Gravity.LEFT, 0, 3, 0, 0)); if (currentEncryptedChat != null) { timeItem = new ImageView(context); timeItem.setPadding(AndroidUtilities.dp(10), AndroidUtilities.dp(10), AndroidUtilities.dp(5), AndroidUtilities.dp(5)); timeItem.setScaleType(ImageView.ScaleType.CENTER); - avatarContainer.addView(timeItem); - timerDrawable = new TimerDrawable(context); - - layoutParams2 = (FrameLayout.LayoutParams) timeItem.getLayoutParams(); - layoutParams2.width = AndroidUtilities.dp(34); - layoutParams2.height = AndroidUtilities.dp(34); - layoutParams2.topMargin = AndroidUtilities.dp(18); - layoutParams2.leftMargin = AndroidUtilities.dp(16); - layoutParams2.gravity = Gravity.TOP | Gravity.LEFT; - timeItem.setLayoutParams(layoutParams2); - timeItem.setImageDrawable(timerDrawable); - + timeItem.setImageDrawable(timerDrawable = new TimerDrawable(context)); + avatarContainer.addView(timeItem, LayoutHelper.createFrame(34, 34, Gravity.TOP | Gravity.LEFT, 16, 18, 0, 0)); timeItem.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (getParentActivity() == null) { return; } - showAlertDialog(AndroidUtilities.buildTTLAlert(getParentActivity(), currentEncryptedChat)); + showDialog(AndroidUtilities.buildTTLAlert(getParentActivity(), currentEncryptedChat).create()); } }); } @@ -940,14 +961,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not nameTextView.setGravity(Gravity.LEFT); nameTextView.setCompoundDrawablePadding(AndroidUtilities.dp(4)); nameTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - avatarContainer.addView(nameTextView); - layoutParams2 = (FrameLayout.LayoutParams) nameTextView.getLayoutParams(); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.leftMargin = AndroidUtilities.dp(54); - layoutParams2.bottomMargin = AndroidUtilities.dp(22); - layoutParams2.gravity = Gravity.BOTTOM; - nameTextView.setLayoutParams(layoutParams2); + avatarContainer.addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.BOTTOM, 54, 0, 0, 22)); onlineTextView = new TextView(context); onlineTextView.setTextColor(0xffd7e8f7); @@ -957,14 +971,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not onlineTextView.setSingleLine(true); onlineTextView.setEllipsize(TextUtils.TruncateAt.END); onlineTextView.setGravity(Gravity.LEFT); - avatarContainer.addView(onlineTextView); - layoutParams2 = (FrameLayout.LayoutParams) onlineTextView.getLayoutParams(); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.leftMargin = AndroidUtilities.dp(54); - layoutParams2.bottomMargin = AndroidUtilities.dp(4); - layoutParams2.gravity = Gravity.BOTTOM; - onlineTextView.setLayoutParams(layoutParams2); + avatarContainer.addView(onlineTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.BOTTOM, 54, 0, 0, 4)); ActionBarMenu menu = actionBar.createMenu(); @@ -982,10 +989,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not headerItem.addSubItem(delete_chat, LocaleController.getString("DeleteChatUser", R.string.DeleteChatUser), 0); } muteItem = headerItem.addSubItem(mute, null, 0); - - LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) headerItem.getLayoutParams(); - layoutParams.rightMargin = AndroidUtilities.dp(-48); - headerItem.setLayoutParams(layoutParams); + ((LinearLayout.LayoutParams) headerItem.getLayoutParams()).setMargins(0, 0, AndroidUtilities.dp(-48), 0); updateTitle(); updateSubtitle(); @@ -1022,18 +1026,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not selectedMessagesCountTextView.setEllipsize(TextUtils.TruncateAt.END); selectedMessagesCountTextView.setPadding(AndroidUtilities.dp(11), 0, 0, AndroidUtilities.dp(2)); selectedMessagesCountTextView.setGravity(Gravity.CENTER_VERTICAL); + actionMode.addView(selectedMessagesCountTextView, LayoutHelper.createLinear(0, LayoutHelper.MATCH_PARENT, 1.0f)); selectedMessagesCountTextView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return true; } }); - actionMode.addView(selectedMessagesCountTextView); - layoutParams = (LinearLayout.LayoutParams) selectedMessagesCountTextView.getLayoutParams(); - layoutParams.weight = 1; - layoutParams.width = 0; - layoutParams.height = LinearLayout.LayoutParams.MATCH_PARENT; - selectedMessagesCountTextView.setLayoutParams(layoutParams); if (currentEncryptedChat == null) { actionModeViews.add(actionMode.addItem(copy, R.drawable.ic_ab_fwd_copy, R.drawable.bar_selector_mode, null, AndroidUtilities.dp(54))); @@ -1052,19 +1051,117 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } checkActionBarMenu(); - fragmentView = new SizeNotifierRelativeLayout(context); - SizeNotifierRelativeLayout contentView = (SizeNotifierRelativeLayout) fragmentView; + fragmentView = new SizeNotifierFrameLayout(context) { + @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); + heightSize -= getPaddingBottom(); + + int inputFieldHeight = 0; + + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + if (child == chatActivityEnterView) { + measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); + inputFieldHeight = child.getMeasuredHeight(); + break; + } + } + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + if (child.getVisibility() == GONE || child == chatActivityEnterView) { + continue; + } + + LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (child == chatListView) { + int contentWidthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY); + int contentHeightSpec = MeasureSpec.makeMeasureSpec(Math.max(AndroidUtilities.dp(10), heightSize - inputFieldHeight + AndroidUtilities.dp(2)), MeasureSpec.EXACTLY); + child.measure(contentWidthSpec, contentHeightSpec); + } else { + measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); + } + } + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + final int count = getChildCount(); + + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (child.getVisibility() == GONE) { + continue; + } + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + + final int width = child.getMeasuredWidth(); + final int height = child.getMeasuredHeight(); + + int childLeft; + int childTop; + + int gravity = lp.gravity; + if (gravity == -1) { + gravity = Gravity.TOP | Gravity.LEFT; + } + + final int absoluteGravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK; + final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; + + switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { + case Gravity.CENTER_HORIZONTAL: + childLeft = (r - l - width) / 2 + lp.leftMargin - lp.rightMargin; + break; + case Gravity.RIGHT: + childLeft = r - width - lp.rightMargin; + break; + case Gravity.LEFT: + default: + childLeft = lp.leftMargin; + } + + switch (verticalGravity) { + case Gravity.TOP: + childTop = lp.topMargin; + break; + case Gravity.CENTER_VERTICAL: + childTop = ((b - getPaddingBottom()) - t - height) / 2 + lp.topMargin - lp.bottomMargin; + break; + case Gravity.BOTTOM: + childTop = ((b - getPaddingBottom()) - t) - height - lp.bottomMargin; + break; + default: + childTop = lp.topMargin; + } + + if (child == mentionListView) { + childTop -= chatActivityEnterView.getMeasuredHeight() - AndroidUtilities.dp(2); + } else if (child == pagedownButton) { + childTop -= chatActivityEnterView.getMeasuredHeight(); + } + child.layout(childLeft, childTop, childLeft + width, childTop + height); + } + + notifyHeightChanged(); + } + }; + + + SizeNotifierFrameLayout contentView = (SizeNotifierFrameLayout) fragmentView; contentView.setBackgroundImage(ApplicationLoader.getCachedWallpaper()); emptyViewContainer = new FrameLayout(context); emptyViewContainer.setPadding(0, 0, 0, AndroidUtilities.dp(48)); emptyViewContainer.setVisibility(View.INVISIBLE); - contentView.addView(emptyViewContainer); - RelativeLayout.LayoutParams layoutParams3 = (RelativeLayout.LayoutParams) emptyViewContainer.getLayoutParams(); - layoutParams3.width = RelativeLayout.LayoutParams.MATCH_PARENT; - layoutParams3.height = RelativeLayout.LayoutParams.MATCH_PARENT; - emptyViewContainer.setLayoutParams(layoutParams3); + contentView.addView(emptyViewContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); emptyViewContainer.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { @@ -1074,7 +1171,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (currentEncryptedChat == null) { TextView emptyView = new TextView(context); - if (currentUser != null && currentUser.id != 777000 && (currentUser.id / 1000 == 333 || currentUser.id % 1000 == 0)) { + if (currentUser != null && currentUser.id != 777000 && currentUser.id != 429000 && (currentUser.id / 1000 == 333 || currentUser.id % 1000 == 0)) { emptyView.setText(LocaleController.getString("GotAQuestion", R.string.GotAQuestion)); } else { emptyView.setText(LocaleController.getString("NoMessages", R.string.NoMessages)); @@ -1084,23 +1181,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not emptyView.setTextColor(0xffffffff); emptyView.setBackgroundResource(ApplicationLoader.isCustomTheme() ? R.drawable.system_black : R.drawable.system_blue); emptyView.setPadding(AndroidUtilities.dp(7), AndroidUtilities.dp(1), AndroidUtilities.dp(7), AndroidUtilities.dp(1)); - emptyViewContainer.addView(emptyView); - layoutParams2 = (FrameLayout.LayoutParams) emptyView.getLayoutParams(); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.gravity = Gravity.CENTER; - emptyView.setLayoutParams(layoutParams2); + emptyViewContainer.addView(emptyView, new FrameLayout.LayoutParams(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); } else { LinearLayout secretChatPlaceholder = new LinearLayout(context); secretChatPlaceholder.setBackgroundResource(ApplicationLoader.isCustomTheme() ? R.drawable.system_black : R.drawable.system_blue); secretChatPlaceholder.setPadding(AndroidUtilities.dp(16), AndroidUtilities.dp(12), AndroidUtilities.dp(16), AndroidUtilities.dp(12)); secretChatPlaceholder.setOrientation(LinearLayout.VERTICAL); - emptyViewContainer.addView(secretChatPlaceholder); - layoutParams2 = (FrameLayout.LayoutParams) secretChatPlaceholder.getLayoutParams(); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.gravity = Gravity.CENTER; - secretChatPlaceholder.setLayoutParams(layoutParams2); + emptyViewContainer.addView(secretChatPlaceholder, new FrameLayout.LayoutParams(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); secretViewStatusTextView = new TextView(context); secretViewStatusTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); @@ -1120,12 +1207,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not secretViewStatusTextView.setText(LocaleController.formatString("EncryptedPlaceholderTitleIncoming", R.string.EncryptedPlaceholderTitleIncoming, currentUser.last_name)); } } - secretChatPlaceholder.addView(secretViewStatusTextView); - layoutParams = (LinearLayout.LayoutParams) secretViewStatusTextView.getLayoutParams(); - layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.CENTER_HORIZONTAL; - secretViewStatusTextView.setLayoutParams(layoutParams); + secretChatPlaceholder.addView(secretViewStatusTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL | Gravity.TOP)); TextView textView = new TextView(context); textView.setText(LocaleController.getString("EncryptedDescriptionTitle", R.string.EncryptedDescriptionTitle)); @@ -1133,24 +1215,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not textView.setTextColor(0xffffffff); textView.setGravity(Gravity.CENTER_HORIZONTAL); textView.setMaxWidth(AndroidUtilities.dp(260)); - secretChatPlaceholder.addView(textView); - layoutParams = (LinearLayout.LayoutParams) textView.getLayoutParams(); - layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.topMargin = AndroidUtilities.dp(8); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - textView.setLayoutParams(layoutParams); + secretChatPlaceholder.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 0, 8, 0, 0)); for (int a = 0; a < 4; a++) { LinearLayout linearLayout = new LinearLayout(context); linearLayout.setOrientation(LinearLayout.HORIZONTAL); - secretChatPlaceholder.addView(linearLayout); - layoutParams = (LinearLayout.LayoutParams) linearLayout.getLayoutParams(); - layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.topMargin = AndroidUtilities.dp(8); - layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; - linearLayout.setLayoutParams(layoutParams); + secretChatPlaceholder.addView(linearLayout, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT, 0, 8, 0, 0)); ImageView imageView = new ImageView(context); imageView.setImageResource(R.drawable.ic_lock_white); @@ -1177,100 +1247,75 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } if (LocaleController.isRTL) { - linearLayout.addView(textView); - linearLayout.addView(imageView); + linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); + linearLayout.addView(imageView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, 8, 3, 0, 0)); } else { - linearLayout.addView(imageView); - linearLayout.addView(textView); + linearLayout.addView(imageView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, 0, 4, 8, 0)); + linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); } - layoutParams = (LinearLayout.LayoutParams) imageView.getLayoutParams(); - layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.rightMargin = LocaleController.isRTL ? 0 : AndroidUtilities.dp(8); - layoutParams.leftMargin = LocaleController.isRTL ? AndroidUtilities.dp(8) : 0; - layoutParams.topMargin = AndroidUtilities.dp(LocaleController.isRTL ? 3 : 4); - imageView.setLayoutParams(layoutParams); - - layoutParams = (LinearLayout.LayoutParams) textView.getLayoutParams(); - layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; - textView.setLayoutParams(layoutParams); } } - chatListView = new LayoutListView(context); - chatListView.setAdapter(chatAdapter = new ChatAdapter(context)); - chatListView.setCacheColorHint(ApplicationLoader.getSelectedColor()); + if (chatActivityEnterView != null) { + chatActivityEnterView.onDestroy(); + } + + chatListView = new RecyclerListView(context); + chatListView.setVerticalScrollBarEnabled(true); + chatListView.setAdapter(chatAdapter = new ChatActivityAdapter(context)); chatListView.setClipToPadding(false); - chatListView.setStackFromBottom(true); chatListView.setPadding(0, AndroidUtilities.dp(4), 0, AndroidUtilities.dp(3)); - chatListView.setDivider(null); - chatListView.setSelector(R.drawable.transparent); + chatListView.setItemAnimator(null); + chatListView.setLayoutAnimation(null); + chatLayoutManager = new LinearLayoutManager(context) { + @Override + public boolean supportsPredictiveItemAnimations() { + return false; + } + }; + chatLayoutManager.setOrientation(LinearLayoutManager.VERTICAL); + chatLayoutManager.setStackFromEnd(true); + chatListView.setLayoutManager(chatLayoutManager); + contentView.addView(chatListView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); chatListView.setOnItemLongClickListener(onItemLongClickListener); chatListView.setOnItemClickListener(onItemClickListener); - contentView.addView(chatListView); - layoutParams3 = (RelativeLayout.LayoutParams) chatListView.getLayoutParams(); - layoutParams3.width = RelativeLayout.LayoutParams.MATCH_PARENT; - layoutParams3.height = RelativeLayout.LayoutParams.MATCH_PARENT; - layoutParams3.bottomMargin = -AndroidUtilities.dp(3); - layoutParams3.addRule(RelativeLayout.ABOVE, id_chat_compose_panel); - chatListView.setLayoutParams(layoutParams3); - chatListView.setOnInterceptTouchEventListener(new LayoutListView.OnInterceptTouchEventListener() { + chatListView.setOnScrollListener(new RecyclerView.OnScrollListener() { + @Override - public boolean onInterceptTouchEvent(MotionEvent event) { - if (actionBar.isActionModeShowed()) { - return false; + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + if (newState != RecyclerView.SCROLL_STATE_DRAGGING && highlightMessageId != Integer.MAX_VALUE) { + highlightMessageId = Integer.MAX_VALUE; + updateVisibleRows(); } - if (event.getAction() == MotionEvent.ACTION_DOWN) { - int x = (int) event.getX(); - int y = (int) event.getY(); - int count = chatListView.getChildCount(); - Rect rect = new Rect(); - for (int a = 0; a < count; a++) { - View view = chatListView.getChildAt(a); - int top = view.getTop(); - int bottom = view.getBottom(); - view.getLocalVisibleRect(rect); - if (top > y || bottom < y) { - continue; - } - if (!(view instanceof ChatMediaCell)) { - break; - } - final ChatMediaCell cell = (ChatMediaCell) view; - final MessageObject messageObject = cell.getMessageObject(); - if (messageObject == null || !messageObject.isSecretPhoto() || !cell.getPhotoImage().isInsideImage(x, y - top)) { - break; - } - File file = FileLoader.getPathToMessage(messageObject.messageOwner); - if (!file.exists()) { - break; - } - startX = x; - startY = y; - chatListView.setOnItemClickListener(null); - openSecretPhotoRunnable = new Runnable() { - @Override - public void run() { - if (openSecretPhotoRunnable == null) { - return; - } - chatListView.requestDisallowInterceptTouchEvent(true); - chatListView.setOnItemLongClickListener(null); - chatListView.setLongClickable(false); - openSecretPhotoRunnable = null; - if (sendSecretMessageRead(messageObject)) { - cell.invalidate(); - } - SecretPhotoViewer.getInstance().setParentActivity(getParentActivity()); - SecretPhotoViewer.getInstance().openPhoto(messageObject); + } + + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + int firstVisibleItem = chatLayoutManager.findFirstVisibleItemPosition(); + int visibleItemCount = Math.abs(chatLayoutManager.findLastVisibleItemPosition() - firstVisibleItem) + 1; + if (visibleItemCount > 0) { + int totalItemCount = chatAdapter.getItemCount(); + if (firstVisibleItem <= 10) { + if (!endReached && !loading) { + if (messagesByDays.size() != 0) { + MessagesController.getInstance().loadMessages(dialog_id, 20, maxMessageId, !cacheEndReaced && startLoadFromMessageId == 0, minDate, classGuid, 0, 0, 0, startLoadFromMessageId == 0); + } else { + MessagesController.getInstance().loadMessages(dialog_id, 20, 0, !cacheEndReaced && startLoadFromMessageId == 0, minDate, classGuid, 0, 0, 0, startLoadFromMessageId == 0); } - }; - AndroidUtilities.runOnUIThread(openSecretPhotoRunnable, 100); - return true; + loading = true; + } + } + if (firstVisibleItem + visibleItemCount >= totalItemCount - 6) { + if (!forward_end_reached && !loadingForward) { + MessagesController.getInstance().loadMessages(dialog_id, 20, minMessageId, startLoadFromMessageId == 0, maxDate, classGuid, 1, 0, 0, startLoadFromMessageId == 0); + loadingForward = true; + } + } + if (firstVisibleItem + visibleItemCount == totalItemCount && forward_end_reached) { + showPagedownButton(false, true); } } - return false; + updateMessagesVisisblePart(); } }); chatListView.setOnTouchListener(new View.OnTouchListener() { @@ -1323,61 +1368,72 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not return false; } }); - chatListView.setOnScrollListener(new AbsListView.OnScrollListener() { - Rect scrollRect = new Rect(); - + chatListView.setOnInterceptTouchListener(new RecyclerListView.OnInterceptTouchListener() { @Override - public void onScrollStateChanged(AbsListView absListView, int i) { - if (i == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL || i == AbsListView.OnScrollListener.SCROLL_STATE_FLING && highlightMessageId != Integer.MAX_VALUE) { - highlightMessageId = Integer.MAX_VALUE; - updateVisibleRows(); + public boolean onInterceptTouchEvent(MotionEvent event) { + if (actionBar.isActionModeShowed()) { + return false; } - } - - @Override - public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) { - if (visibleItemCount > 0) { - if (firstVisibleItem <= 10) { - if (!endReached && !loading) { - if (messagesByDays.size() != 0) { - MessagesController.getInstance().loadMessages(dialog_id, 20, maxMessageId, !cacheEndReaced && startLoadFromMessageId == 0, minDate, classGuid, 0, 0, 0, startLoadFromMessageId == 0); - } else { - MessagesController.getInstance().loadMessages(dialog_id, 20, 0, !cacheEndReaced && startLoadFromMessageId == 0, minDate, classGuid, 0, 0, 0, startLoadFromMessageId == 0); + if (event.getAction() == MotionEvent.ACTION_DOWN) { + int x = (int) event.getX(); + int y = (int) event.getY(); + int count = chatListView.getChildCount(); + Rect rect = new Rect(); + for (int a = 0; a < count; a++) { + View view = chatListView.getChildAt(a); + int top = view.getTop(); + int bottom = view.getBottom(); + view.getLocalVisibleRect(rect); + if (top > y || bottom < y) { + continue; + } + if (!(view instanceof ChatMediaCell)) { + break; + } + final ChatMediaCell cell = (ChatMediaCell) view; + final MessageObject messageObject = cell.getMessageObject(); + if (messageObject == null || messageObject.isSending() || !messageObject.isSecretPhoto() || !cell.getPhotoImage().isInsideImage(x, y - top)) { + break; + } + File file = FileLoader.getPathToMessage(messageObject.messageOwner); + if (!file.exists()) { + break; + } + startX = x; + startY = y; + chatListView.setOnItemClickListener(null); + openSecretPhotoRunnable = new Runnable() { + @Override + public void run() { + if (openSecretPhotoRunnable == null) { + return; + } + chatListView.requestDisallowInterceptTouchEvent(true); + chatListView.setOnItemLongClickListener(null); + chatListView.setLongClickable(false); + openSecretPhotoRunnable = null; + if (sendSecretMessageRead(messageObject)) { + cell.invalidate(); + } + SecretPhotoViewer.getInstance().setParentActivity(getParentActivity()); + SecretPhotoViewer.getInstance().openPhoto(messageObject); } - loading = true; - } - } - if (firstVisibleItem + visibleItemCount >= totalItemCount - 6) { - if (!forward_end_reached && !loadingForward) { - MessagesController.getInstance().loadMessages(dialog_id, 20, minMessageId, startLoadFromMessageId == 0, maxDate, classGuid, 1, 0, 0, startLoadFromMessageId == 0); - loadingForward = true; - } - } - if (firstVisibleItem + visibleItemCount == totalItemCount && forward_end_reached) { - showPagedownButton(false, true); - } - } - for (int a = 0; a < visibleItemCount; a++) { - View view = absListView.getChildAt(a); - if (view instanceof ChatMessageCell) { - ChatMessageCell messageCell = (ChatMessageCell) view; - messageCell.getLocalVisibleRect(scrollRect); - messageCell.setVisiblePart(scrollRect.top, scrollRect.bottom - scrollRect.top); + }; + AndroidUtilities.runOnUIThread(openSecretPhotoRunnable, 100); + return true; } } + return false; } }); progressView = new FrameLayout(context); progressView.setVisibility(View.INVISIBLE); - progressView.setBackgroundResource(ApplicationLoader.isCustomTheme() ? R.drawable.system_loader2 : R.drawable.system_loader1); - contentView.addView(progressView); - layoutParams3 = (RelativeLayout.LayoutParams) progressView.getLayoutParams(); - layoutParams3.width = AndroidUtilities.dp(36); - layoutParams3.height = AndroidUtilities.dp(36); - layoutParams3.bottomMargin = AndroidUtilities.dp(48); - layoutParams3.addRule(RelativeLayout.CENTER_IN_PARENT); - progressView.setLayoutParams(layoutParams3); + contentView.addView(progressView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT, 0, 0, 0, 48)); + + View view = new View(context); + view.setBackgroundResource(ApplicationLoader.isCustomTheme() ? R.drawable.system_loader2 : R.drawable.system_loader1); + progressView.addView(view, LayoutHelper.createFrame(36, 36, Gravity.CENTER)); ProgressBar progressBar = new ProgressBar(context); try { @@ -1387,12 +1443,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } progressBar.setIndeterminate(true); AndroidUtilities.setProgressBarAnimationDuration(progressBar, 1500); - progressView.addView(progressBar); - layoutParams2 = (FrameLayout.LayoutParams) progressBar.getLayoutParams(); - layoutParams2.width = AndroidUtilities.dp(32); - layoutParams2.height = AndroidUtilities.dp(32); - layoutParams2.gravity = Gravity.CENTER; - progressBar.setLayoutParams(layoutParams2); + progressView.addView(progressBar, LayoutHelper.createFrame(32, 32, Gravity.CENTER)); if (currentEncryptedChat == null && !isBroadcast) { mentionListView = new ListView(context); @@ -1405,19 +1456,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (Build.VERSION.SDK_INT > 8) { mentionListView.setOverScrollMode(ListView.OVER_SCROLL_NEVER); } - contentView.addView(mentionListView); - layoutParams3 = (RelativeLayout.LayoutParams) mentionListView.getLayoutParams(); - layoutParams3.width = RelativeLayout.LayoutParams.MATCH_PARENT; - layoutParams3.height = AndroidUtilities.dp(110); - layoutParams3.topMargin = -AndroidUtilities.dp(108); - layoutParams3.addRule(RelativeLayout.ALIGN_TOP, id_chat_compose_panel); - mentionListView.setLayoutParams(layoutParams3); + contentView.addView(mentionListView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 110, Gravity.LEFT | Gravity.BOTTOM)); - mentionListView.setAdapter(mentionsAdapter = new MentionsAdapter(context, new MentionsAdapter.MentionsAdapterDelegate() { + mentionListView.setAdapter(mentionsAdapter = new MentionsAdapter(context, false, new MentionsAdapter.MentionsAdapterDelegate() { @Override public void needChangePanelVisibility(boolean show) { if (show) { - RelativeLayout.LayoutParams layoutParams3 = (RelativeLayout.LayoutParams) mentionListView.getLayoutParams(); + FrameLayout.LayoutParams layoutParams3 = (FrameLayout.LayoutParams) mentionListView.getLayoutParams(); int height = 36 * Math.min(3, mentionsAdapter.getCount()) + (mentionsAdapter.getCount() > 3 ? 18 : 0); layoutParams3.height = AndroidUtilities.dp(2 + height); layoutParams3.topMargin = -AndroidUtilities.dp(height); @@ -1522,7 +1567,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); return true; } return false; @@ -1530,19 +1575,11 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not }); } - if (chatActivityEnterView != null) { - chatActivityEnterView.onDestroy(); - } chatActivityEnterView = new ChatActivityEnterView(getParentActivity(), contentView, this, true); chatActivityEnterView.setDialogId(dialog_id); chatActivityEnterView.addToAttachLayout(menuItem); chatActivityEnterView.setId(id_chat_compose_panel); - contentView.addView(chatActivityEnterView); - layoutParams3 = (RelativeLayout.LayoutParams) chatActivityEnterView.getLayoutParams(); - layoutParams3.width = RelativeLayout.LayoutParams.MATCH_PARENT; - layoutParams3.height = RelativeLayout.LayoutParams.WRAP_CONTENT; - layoutParams3.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); - chatActivityEnterView.setLayoutParams(layoutParams3); + contentView.addView(chatActivityEnterView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.BOTTOM)); chatActivityEnterView.setDelegate(new ChatActivityEnterView.ChatActivityEnterViewDelegate() { @Override public void onMessageSend(String message) { @@ -1551,9 +1588,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (mentionsAdapter != null) { mentionsAdapter.addHashtagsFromMessage(message); } - if (message != null) { - NotificationsController.getInstance().playOutChatSound(); - } } @Override @@ -1568,25 +1602,27 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not AndroidUtilities.cancelRunOnUIThread(waitingForCharaterEnterRunnable); waitingForCharaterEnterRunnable = null; } - if (bigChange) { - searchLinks(text, true); - } else { - waitingForCharaterEnterRunnable = new Runnable() { - @Override - public void run() { - if (this == waitingForCharaterEnterRunnable) { - searchLinks(text, false); - waitingForCharaterEnterRunnable = null; + if (chatActivityEnterView.isMessageWebPageSearchEnabled()) { + if (bigChange) { + searchLinks(text, true); + } else { + waitingForCharaterEnterRunnable = new Runnable() { + @Override + public void run() { + if (this == waitingForCharaterEnterRunnable) { + searchLinks(text, false); + waitingForCharaterEnterRunnable = null; + } } - } - }; - AndroidUtilities.runOnUIThread(waitingForCharaterEnterRunnable, 1000); + }; + AndroidUtilities.runOnUIThread(waitingForCharaterEnterRunnable, 3000); + } } } @Override public void needSendTyping() { - MessagesController.getInstance().sendTyping(dialog_id, classGuid); + MessagesController.getInstance().sendTyping(dialog_id, 0, classGuid); } @Override @@ -1632,6 +1668,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not mentionListView.setVisibility(View.VISIBLE); } } + updateMessagesVisisblePart(); } }); @@ -1641,32 +1678,16 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not View lineView = new View(context); lineView.setBackgroundColor(0xffe8e8e8); - replyLayout.addView(lineView); - layoutParams2 = (FrameLayout.LayoutParams) lineView.getLayoutParams(); - layoutParams2.gravity = Gravity.BOTTOM; - layoutParams2.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams2.height = AndroidUtilities.dp(1); - lineView.setLayoutParams(layoutParams2); + replyLayout.addView(lineView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 1, Gravity.BOTTOM | Gravity.LEFT)); replyIconImageView = new ImageView(context); replyIconImageView.setScaleType(ImageView.ScaleType.CENTER); - replyLayout.addView(replyIconImageView); - layoutParams2 = (FrameLayout.LayoutParams) replyIconImageView.getLayoutParams(); - layoutParams2.gravity = Gravity.TOP | Gravity.LEFT; - layoutParams2.width = AndroidUtilities.dp(52); - layoutParams2.height = AndroidUtilities.dp(46); - replyIconImageView.setLayoutParams(layoutParams2); + replyLayout.addView(replyIconImageView, LayoutHelper.createFrame(52, 46, Gravity.TOP | Gravity.LEFT)); ImageView imageView = new ImageView(context); imageView.setImageResource(R.drawable.delete_reply); imageView.setScaleType(ImageView.ScaleType.CENTER); - replyLayout.addView(imageView); - layoutParams2 = (FrameLayout.LayoutParams) imageView.getLayoutParams(); - layoutParams2.topMargin = AndroidUtilities.dp(0.5f); - layoutParams2.width = AndroidUtilities.dp(52); - layoutParams2.height = AndroidUtilities.dp(46); - layoutParams2.gravity = Gravity.RIGHT | Gravity.TOP; - imageView.setLayoutParams(layoutParams2); + replyLayout.addView(imageView, LayoutHelper.createFrame(52, 46, Gravity.RIGHT | Gravity.TOP, 0, 0.5f, 0, 0)); imageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -1684,15 +1705,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not replyNameTextView.setSingleLine(true); replyNameTextView.setEllipsize(TextUtils.TruncateAt.END); replyNameTextView.setMaxLines(1); - replyLayout.addView(replyNameTextView); - layoutParams2 = (FrameLayout.LayoutParams) replyNameTextView.getLayoutParams(); - layoutParams2.leftMargin = AndroidUtilities.dp(52); - layoutParams2.rightMargin = AndroidUtilities.dp(52); - layoutParams2.topMargin = AndroidUtilities.dp(4); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.gravity = Gravity.TOP | Gravity.LEFT; - replyNameTextView.setLayoutParams(layoutParams2); + replyLayout.addView(replyNameTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.LEFT, 52, 4, 52, 0)); replyObjectTextView = new TextView(context); replyObjectTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); @@ -1700,37 +1713,17 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not replyObjectTextView.setSingleLine(true); replyObjectTextView.setEllipsize(TextUtils.TruncateAt.END); replyObjectTextView.setMaxLines(1); - replyLayout.addView(replyObjectTextView); - layoutParams2 = (FrameLayout.LayoutParams) replyObjectTextView.getLayoutParams(); - layoutParams2.leftMargin = AndroidUtilities.dp(52); - layoutParams2.rightMargin = AndroidUtilities.dp(52); - layoutParams2.topMargin = AndroidUtilities.dp(22); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.gravity = Gravity.TOP | Gravity.LEFT; - replyObjectTextView.setLayoutParams(layoutParams2); + replyLayout.addView(replyObjectTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.LEFT, 52, 22, 52, 0)); replyImageView = new BackupImageView(context); - replyLayout.addView(replyImageView); - layoutParams2 = (FrameLayout.LayoutParams) replyImageView.getLayoutParams(); - layoutParams2.leftMargin = AndroidUtilities.dp(52); - layoutParams2.topMargin = AndroidUtilities.dp(6); - layoutParams2.width = AndroidUtilities.dp(34); - layoutParams2.height = AndroidUtilities.dp(34); - layoutParams2.gravity = Gravity.TOP | Gravity.LEFT; - replyImageView.setLayoutParams(layoutParams2); + replyLayout.addView(replyImageView, LayoutHelper.createFrame(34, 34, Gravity.TOP | Gravity.LEFT, 52, 6, 0, 0)); stickersPanel = new FrameLayout(context); stickersPanel.setVisibility(View.GONE); - contentView.addView(stickersPanel); - layoutParams3 = (RelativeLayout.LayoutParams) stickersPanel.getLayoutParams(); - layoutParams3.width = RelativeLayout.LayoutParams.WRAP_CONTENT; - layoutParams3.height = AndroidUtilities.dp(81.5f); - layoutParams3.bottomMargin = AndroidUtilities.dp(38); - layoutParams3.addRule(RelativeLayout.ALIGN_BOTTOM, id_chat_compose_panel); - stickersPanel.setLayoutParams(layoutParams3); + contentView.addView(stickersPanel, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 81.5f, Gravity.LEFT | Gravity.BOTTOM, 0, 0, 0, 38)); stickersListView = new RecyclerListView(context); + stickersListView.setDisallowInterceptTouchEvents(true); LinearLayoutManager layoutManager = new LinearLayoutManager(context); layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); stickersListView.setLayoutManager(layoutManager); @@ -1738,12 +1731,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (Build.VERSION.SDK_INT >= 9) { stickersListView.setOverScrollMode(RecyclerListView.OVER_SCROLL_NEVER); } - stickersPanel.addView(stickersListView); - layoutParams2 = (FrameLayout.LayoutParams) stickersListView.getLayoutParams(); - layoutParams2.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams2.height = AndroidUtilities.dp(78); - stickersListView.setLayoutParams(layoutParams2); + stickersPanel.addView(stickersListView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 78)); if (currentEncryptedChat == null || currentEncryptedChat != null && AndroidUtilities.getPeerLayerVersion(currentEncryptedChat.layer) >= 23) { + chatActivityEnterView.setAllowStickers(true); if (stickersAdapter != null) { stickersAdapter.destroy(); } @@ -1788,66 +1778,22 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } })); - stickersListView.addOnItemTouchListener(new RecyclerListView.RecyclerListViewItemClickListener(context, new RecyclerListView.OnItemClickListener() { + stickersListView.setOnItemClickListener(new RecyclerListView.OnItemClickListener() { @Override public void onItemClick(View view, int position) { TLRPC.Document document = stickersAdapter.getItem(position); if (document instanceof TLRPC.TL_document) { - if (currentEncryptedChat != null && document.thumb instanceof TLRPC.TL_photoSize) { - File file = FileLoader.getPathToAttach(document.thumb, true); - if (file.exists()) { - try { - int len = (int) file.length(); - byte[] arr = new byte[(int) file.length()]; - RandomAccessFile reader = new RandomAccessFile(file, "r"); - reader.readFully(arr); - TLRPC.TL_document newDocument = new TLRPC.TL_document(); - newDocument.thumb = new TLRPC.TL_photoCachedSize(); - newDocument.thumb.location = document.thumb.location; - newDocument.thumb.size = document.thumb.size; - newDocument.thumb.w = document.thumb.w; - newDocument.thumb.h = document.thumb.h; - newDocument.thumb.type = document.thumb.type; - newDocument.thumb.bytes = arr; - - newDocument.id = document.id; - newDocument.access_hash = document.access_hash; - newDocument.date = document.date; - newDocument.mime_type = document.mime_type; - newDocument.size = document.size; - newDocument.dc_id = document.dc_id; - newDocument.attributes = document.attributes; - document = newDocument; - } catch (Exception e) { - FileLog.e("tmessages", e); - } - } - } - for (int a = 0; a < document.attributes.size(); a++) { - TLRPC.DocumentAttribute attribute = document.attributes.get(a); - if (attribute instanceof TLRPC.TL_documentAttributeSticker) { - document.attributes.remove(a); - document.attributes.add(new TLRPC.TL_documentAttributeSticker_old()); - break; - } - } - SendMessagesHelper.getInstance().sendMessage((TLRPC.TL_document) document, null, null, dialog_id, replyingMessageObject); + SendMessagesHelper.getInstance().sendSticker(document, dialog_id, replyingMessageObject); showReplyPanel(false, null, null, null, false, true); } chatActivityEnterView.setFieldText(""); } - })); + }); } imageView = new ImageView(context); imageView.setImageResource(R.drawable.stickers_back_arrow); - stickersPanel.addView(imageView); - layoutParams2 = (FrameLayout.LayoutParams) imageView.getLayoutParams(); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.gravity = Gravity.BOTTOM; - layoutParams2.leftMargin = AndroidUtilities.dp(53); - imageView.setLayoutParams(layoutParams2); + stickersPanel.addView(imageView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | Gravity.LEFT, 53, 0, 0, 0)); bottomOverlay = new FrameLayout(context); bottomOverlay.setBackgroundColor(0xffffffff); @@ -1855,32 +1801,17 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not bottomOverlay.setFocusable(true); bottomOverlay.setFocusableInTouchMode(true); bottomOverlay.setClickable(true); - contentView.addView(bottomOverlay); - layoutParams3 = (RelativeLayout.LayoutParams) bottomOverlay.getLayoutParams(); - layoutParams3.width = RelativeLayout.LayoutParams.MATCH_PARENT; - layoutParams3.height = AndroidUtilities.dp(48); - layoutParams3.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); - bottomOverlay.setLayoutParams(layoutParams3); + contentView.addView(bottomOverlay, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM)); bottomOverlayText = new TextView(context); bottomOverlayText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); bottomOverlayText.setTextColor(0xff7f7f7f); - bottomOverlay.addView(bottomOverlayText); - layoutParams2 = (FrameLayout.LayoutParams) bottomOverlayText.getLayoutParams(); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.gravity = Gravity.CENTER; - bottomOverlayText.setLayoutParams(layoutParams2); + bottomOverlay.addView(bottomOverlayText, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); bottomOverlayChat = new FrameLayout(context); bottomOverlayChat.setBackgroundColor(0xfffbfcfd); bottomOverlayChat.setVisibility(View.INVISIBLE); - contentView.addView(bottomOverlayChat); - layoutParams3 = (RelativeLayout.LayoutParams) bottomOverlayChat.getLayoutParams(); - layoutParams3.width = RelativeLayout.LayoutParams.MATCH_PARENT; - layoutParams3.height = AndroidUtilities.dp(48); - layoutParams3.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); - bottomOverlayChat.setLayoutParams(layoutParams3); + contentView.addView(bottomOverlayChat, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM)); bottomOverlayChat.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -1908,32 +1839,19 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not }); } builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } }); bottomOverlayChatText = new TextView(context); bottomOverlayChatText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); bottomOverlayChatText.setTextColor(0xff3e6fa1); - bottomOverlayChat.addView(bottomOverlayChatText); - layoutParams2 = (FrameLayout.LayoutParams) bottomOverlayChatText.getLayoutParams(); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.gravity = Gravity.CENTER; - bottomOverlayChatText.setLayoutParams(layoutParams2); + bottomOverlayChat.addView(bottomOverlayChatText, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); pagedownButton = new ImageView(context); pagedownButton.setVisibility(View.INVISIBLE); pagedownButton.setImageResource(R.drawable.pagedown); - contentView.addView(pagedownButton); - layoutParams3 = (RelativeLayout.LayoutParams) pagedownButton.getLayoutParams(); - layoutParams3.width = RelativeLayout.LayoutParams.WRAP_CONTENT; - layoutParams3.height = RelativeLayout.LayoutParams.WRAP_CONTENT; - layoutParams3.rightMargin = AndroidUtilities.dp(6); - layoutParams3.bottomMargin = AndroidUtilities.dp(4); - layoutParams3.addRule(RelativeLayout.ABOVE, id_chat_compose_panel); - layoutParams3.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); - pagedownButton.setLayoutParams(layoutParams3); + contentView.addView(pagedownButton, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.RIGHT | Gravity.BOTTOM, 0, 0, 6, 4)); pagedownButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -1960,6 +1878,33 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not return fragmentView; } + private boolean searchForHttpInText(CharSequence string) { + int len = string.length(); + int seqLen = 0; + for (int a = 0; a < len; a++) { + char ch = string.charAt(a); + if (seqLen == 0 && (ch == 'h' || ch == 'H')) { + seqLen++; + } else if ((seqLen == 1 || seqLen == 2) && (ch == 't' || ch == 'T')) { + seqLen++; + } else if (seqLen == 3 && (ch == 'p' || ch == 'P')) { + seqLen++; + } else if (seqLen == 4 && (ch == 's' || ch == 'S')) { + seqLen++; + } else if ((seqLen == 4 || seqLen == 5) && ch == ':') { + seqLen++; + } else if ((seqLen == 5 || seqLen == 6 || seqLen == 7) && ch == '/') { + if (seqLen == 6 || seqLen == 7) { + return true; + } + seqLen++; + } else if (seqLen != 0) { + seqLen = 0; + } + } + return false; + } + private void searchLinks(CharSequence charSequence, boolean force) { if (currentEncryptedChat != null) { return; @@ -1988,7 +1933,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not pendingLinkSearchString = null; showReplyPanel(false, null, null, foundWebPage, false, true); } - if (charSequence.length() == 0 || TextUtils.indexOf(charSequence, "http") == -1 && TextUtils.indexOf(charSequence, ".com/") == -1) { + if (charSequence.length() < 13 || !searchForHttpInText(charSequence)) { return; } final TLRPC.TL_messages_getWebPagePreview req = new TLRPC.TL_messages_getWebPagePreview(); @@ -2156,7 +2101,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not replyObjectTextView.setText(LocaleController.formatPluralString("ForwardedSticker", messageObjects.size())); } else if (type == 8 || type == 9) { if (messageObjects.size() == 1) { - String name = null; + String name; if ((name = FileLoader.getDocumentFileName(messageObjects.get(0).messageOwner.media.document)).length() != 0) { replyObjectTextView.setText(name); } @@ -2234,15 +2179,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private void moveScrollToLastMessage() { if (chatListView != null) { - chatListView.setSelectionFromTop(messages.size() - 1, -100000 - chatListView.getPaddingTop()); - chatListView.setForceTop(-100000 - chatListView.getPaddingTop()); - chatListView.post(new Runnable() { - @Override - public void run() { - chatListView.setForceTop(-100000 - chatListView.getPaddingTop()); - chatListView.setSelectionFromTop(messages.size() - 1, -100000 - chatListView.getPaddingTop()); - } - }); + chatLayoutManager.scrollToPositionWithOffset(messages.size() - 1, -100000 - chatListView.getPaddingTop()); } } @@ -2257,7 +2194,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private void scrollToLastMessage() { if (forward_end_reached && first_unread_id == 0 && startLoadFromMessageId == 0) { - chatListView.setSelectionFromTop(messages.size() - 1, -100000 - chatListView.getPaddingTop()); + chatLayoutManager.scrollToPositionWithOffset(messages.size() - 1, -100000 - chatListView.getPaddingTop()); } else { messages.clear(); messagesByDays.clear(); @@ -2282,6 +2219,21 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } + private void updateMessagesVisisblePart() { + if (chatListView == null) { + return; + } + int count = chatListView.getChildCount(); + for (int a = 0; a < count; a++) { + View view = chatListView.getChildAt(a); + if (view instanceof ChatMessageCell) { + ChatMessageCell messageCell = (ChatMessageCell) view; + messageCell.getLocalVisibleRect(scrollRect); + messageCell.setVisiblePart(scrollRect.top, scrollRect.bottom - scrollRect.top); + } + } + } + private void scrollToMessageId(int id, int fromMessageId, boolean select) { returnToMessageId = fromMessageId; needSelectFromMessageId = select; @@ -2298,9 +2250,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } final int yOffset = Math.max(0, (chatListView.getHeight() - object.getApproximateHeight()) / 2); if (messages.get(messages.size() - 1) == object) { - chatListView.setSelectionFromTop(0, AndroidUtilities.dp(-11) + yOffset); + chatLayoutManager.scrollToPositionWithOffset(0, AndroidUtilities.dp(-11) + yOffset); } else { - chatListView.setSelectionFromTop(messages.size() - messages.indexOf(object), AndroidUtilities.dp(-11) + yOffset); + chatLayoutManager.scrollToPositionWithOffset(messages.size() - messages.indexOf(object), AndroidUtilities.dp(-11) + yOffset); } updateVisibleRows(); showPagedownButton(true, true); @@ -2476,7 +2428,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (!messageObject.isMediaEmpty()) { return 0; } else { - return 7; + return 20; } } else { return -1; @@ -2494,6 +2446,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVideo || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaPhoto || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument) { + if (messageObject.isSticker()) { + TLRPC.InputStickerSet inputStickerSet = messageObject.getInputStickerSet(); + if (inputStickerSet != null && !StickersQuery.isStickerPackInstalled(inputStickerSet.id)) { + return 7; + } + } boolean canSave = false; if (messageObject.messageOwner.attachPath != null && messageObject.messageOwner.attachPath.length() != 0) { File f = new File(messageObject.messageOwner.attachPath); @@ -2537,10 +2495,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (!messageObject.isMediaEmpty()) { return 0; } else { - return 7; + return 20; } } else if (messageObject.type == 10 || messageObject.type == 11) { - if (messageObject.isSending()) { + if (messageObject.getId() == 0 || messageObject.isSending()) { return -1; } else { return 1; @@ -2550,6 +2508,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVideo || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaPhoto || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument) { + if (messageObject.isSticker()) { + TLRPC.InputStickerSet inputStickerSet = messageObject.getInputStickerSet(); + if (inputStickerSet != null && !StickersQuery.isStickerPackInstalled(inputStickerSet.id)) { + return 7; + } + } boolean canSave = false; if (messageObject.messageOwner.attachPath != null && messageObject.messageOwner.attachPath.length() != 0) { File f = new File(messageObject.messageOwner.attachPath); @@ -2616,7 +2580,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not int type = getMessageType(message); - if (type < 2 || type == 7) { + if (type < 2 || type == 8) { return; } addToSelectedMessages(message); @@ -2722,23 +2686,62 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } if (start) { try { - if (onlineTextView != null) { - onlineTextView.setCompoundDrawablesWithIntrinsicBounds(typingDotsDrawable, null, null, null); - onlineTextView.setCompoundDrawablePadding(AndroidUtilities.dp(4)); - } - if (typingDotsDrawable != null) { - typingDotsDrawable.start(); + Integer type = MessagesController.getInstance().printingStringsTypes.get(dialog_id); + if (type == 0) { + if (lastStatusDrawable == 1) { + return; + } + lastStatusDrawable = 1; + if (onlineTextView != null) { + onlineTextView.setCompoundDrawablesWithIntrinsicBounds(typingDotsDrawable, null, null, null); + onlineTextView.setCompoundDrawablePadding(AndroidUtilities.dp(4)); + + typingDotsDrawable.start(); + recordStatusDrawable.stop(); + sendingFileDrawable.stop(); + } + } else if (type == 1) { + if (lastStatusDrawable == 2) { + return; + } + lastStatusDrawable = 2; + if (onlineTextView != null) { + onlineTextView.setCompoundDrawablesWithIntrinsicBounds(recordStatusDrawable, null, null, null); + onlineTextView.setCompoundDrawablePadding(AndroidUtilities.dp(4)); + + recordStatusDrawable.start(); + typingDotsDrawable.stop(); + sendingFileDrawable.stop(); + } + } else if (type == 2) { + if (lastStatusDrawable == 3) { + return; + } + lastStatusDrawable = 3; + if (onlineTextView != null) { + onlineTextView.setCompoundDrawablesWithIntrinsicBounds(sendingFileDrawable, null, null, null); + onlineTextView.setCompoundDrawablePadding(AndroidUtilities.dp(4)); + + sendingFileDrawable.start(); + typingDotsDrawable.stop(); + recordStatusDrawable.stop(); + } } } catch (Exception e) { FileLog.e("tmessages", e); } } else { + if (lastStatusDrawable == 0) { + return; + } + lastStatusDrawable = 0; if (onlineTextView != null) { onlineTextView.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null); onlineTextView.setCompoundDrawablePadding(0); - } - if (typingDotsDrawable != null) { + typingDotsDrawable.stop(); + recordStatusDrawable.stop(); + sendingFileDrawable.stop(); } } } @@ -2772,14 +2775,14 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } - public boolean openVideoEditor(String videoPath, boolean removeLast) { + public boolean openVideoEditor(String videoPath, boolean removeLast, boolean animated) { Bundle args = new Bundle(); args.putString("videoPath", videoPath); VideoEditorActivity fragment = new VideoEditorActivity(args); fragment.setDelegate(new VideoEditorActivity.VideoEditorActivityDelegate() { @Override public void didFinishEditVideo(String videoPath, long startTime, long endTime, int resultWidth, int resultHeight, int rotationValue, int originalWidth, int originalHeight, int bitrate, long estimatedSize, long estimatedDuration) { - TLRPC.VideoEditedInfo videoEditedInfo = new TLRPC.VideoEditedInfo(); + VideoEditedInfo videoEditedInfo = new VideoEditedInfo(); videoEditedInfo.startTime = startTime; videoEditedInfo.endTime = endTime; videoEditedInfo.rotationValue = rotationValue; @@ -2799,7 +2802,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not showReplyPanel(false, null, null, null, false, true); return false; } - parentLayout.presentFragment(fragment, removeLast, true, true); + parentLayout.presentFragment(fragment, removeLast, !animated, true); return true; } @@ -2835,55 +2838,64 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } catch (Exception e) { FileLog.e("tmessages", e); } - arrayList.add(new MediaController.PhotoEntry(0, 0, 0, currentPicturePath, orientation)); + arrayList.add(new MediaController.PhotoEntry(0, 0, 0, currentPicturePath, orientation, false)); PhotoViewer.getInstance().openPhotoForSelect(arrayList, 0, 2, new PhotoViewer.EmptyPhotoViewerProvider() { @Override public void sendButtonPressed(int index) { MediaController.PhotoEntry photoEntry = (MediaController.PhotoEntry) arrayList.get(0); if (photoEntry.imagePath != null) { - SendMessagesHelper.prepareSendingPhoto(photoEntry.imagePath, null, dialog_id, replyingMessageObject); + SendMessagesHelper.prepareSendingPhoto(photoEntry.imagePath, null, dialog_id, replyingMessageObject, photoEntry.caption); showReplyPanel(false, null, null, null, false, true); } else if (photoEntry.path != null) { - SendMessagesHelper.prepareSendingPhoto(photoEntry.path, null, dialog_id, replyingMessageObject); + SendMessagesHelper.prepareSendingPhoto(photoEntry.path, null, dialog_id, replyingMessageObject, photoEntry.caption); showReplyPanel(false, null, null, null, false, true); } } - }); - Utilities.addMediaToGallery(currentPicturePath); + }, this); + AndroidUtilities.addMediaToGallery(currentPicturePath); currentPicturePath = null; } else if (requestCode == 1) { if (data == null || data.getData() == null) { showAttachmentError(); return; } - SendMessagesHelper.prepareSendingPhoto(null, data.getData(), dialog_id, replyingMessageObject); + Uri uri = data.getData(); + if (uri.toString().contains("video")) { + String videoPath = null; + try { + videoPath = AndroidUtilities.getPath(uri); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + if (videoPath == null) { + showAttachmentError(); + } + if (Build.VERSION.SDK_INT >= 16) { + if (paused) { + startVideoEdit = videoPath; + } else { + openVideoEditor(videoPath, false, false); + } + } else { + SendMessagesHelper.prepareSendingVideo(videoPath, 0, 0, 0, 0, null, dialog_id, replyingMessageObject); + showReplyPanel(false, null, null, null, false, true); + } + } else { + SendMessagesHelper.prepareSendingPhoto(null, uri, dialog_id, replyingMessageObject, null); + } showReplyPanel(false, null, null, null, false, true); } else if (requestCode == 2) { String videoPath = null; if (data != null) { Uri uri = data.getData(); - boolean fromCamera = false; - if (uri != null && uri.getScheme() != null) { - fromCamera = uri.getScheme().contains("file"); - } else if (uri == null) { - fromCamera = true; - } - if (fromCamera) { - if (uri != null) { - videoPath = uri.getPath(); - } else { - videoPath = currentPicturePath; - } - Utilities.addMediaToGallery(currentPicturePath); - currentPicturePath = null; + if (uri != null) { + videoPath = uri.getPath(); } else { - try { - videoPath = Utilities.getPath(uri); - } catch (Exception e) { - FileLog.e("tmessages", e); - } + videoPath = currentPicturePath; } + AndroidUtilities.addMediaToGallery(currentPicturePath); + currentPicturePath = null; } if (videoPath == null && currentPicturePath != null) { File f = new File(currentPicturePath); @@ -2896,7 +2908,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (paused) { startVideoEdit = videoPath; } else { - openVideoEditor(videoPath, false); + openVideoEditor(videoPath, false, false); } } else { SendMessagesHelper.prepareSendingVideo(videoPath, 0, 0, 0, 0, null, dialog_id, replyingMessageObject); @@ -2907,7 +2919,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not showAttachmentError(); return; } - String tempPath = Utilities.getPath(data.getData()); + String tempPath = AndroidUtilities.getPath(data.getData()); String originalPath = tempPath; if (tempPath == null) { originalPath = data.toString(); @@ -2935,17 +2947,18 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not currentPicturePath = args.getString("path"); } - private void removeUnreadPlane(boolean reload) { + private void removeUnreadPlane() { if (unreadMessageObject != null) { - messages.remove(unreadMessageObject); forward_end_reached = true; first_unread_id = 0; last_message_id = 0; unread_to_load = 0; - unreadMessageObject = null; - if (reload) { - chatAdapter.notifyDataSetChanged(); + if (chatAdapter != null) { + chatAdapter.removeMessageObject(unreadMessageObject); + } else { + messages.remove(unreadMessageObject); } + unreadMessageObject = null; } } @@ -2957,7 +2970,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not @Override public void didReceivedNotification(int id, final Object... args) { if (id == NotificationCenter.messagesDidLoaded) { - long did = (Long) args[0]; if (did == dialog_id) { loadsCount++; @@ -3113,9 +3125,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not last_message_id = 0; first_message_id = 0; startLoadFromMessageId = 0; + chatAdapter.notifyItemChanged(chatAdapter.getItemCount() - 1); + newRowsCount--; + } + if (newRowsCount != 0) { + chatAdapter.notifyItemRangeInserted(chatAdapter.getItemCount() - 2, newRowsCount); } - - chatAdapter.notifyDataSetChanged(); loadingForward = false; } else { if (messArr.size() != count) { @@ -3136,37 +3151,44 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not chatAdapter.notifyDataSetChanged(); if (scrollToMessage != null) { final int yOffset = scrollToMessageMiddleScreen ? Math.max(0, (chatListView.getHeight() - scrollToMessage.getApproximateHeight()) / 2) : 0; - if (messages.get(messages.size() - 1) == scrollToMessage) { - chatListView.setSelectionFromTop(0, AndroidUtilities.dp(-11) + yOffset); - } else { - chatListView.setSelectionFromTop(messages.size() - messages.indexOf(scrollToMessage), AndroidUtilities.dp(-11) + yOffset); - } - ViewTreeObserver obs = chatListView.getViewTreeObserver(); - obs.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - if (!messages.isEmpty()) { - if (messages.get(messages.size() - 1) == scrollToMessage) { - chatListView.setSelectionFromTop(0, AndroidUtilities.dp(-11) + yOffset); - } else { - chatListView.setSelectionFromTop(messages.size() - messages.indexOf(scrollToMessage), AndroidUtilities.dp(-11) + yOffset); - } - } - chatListView.getViewTreeObserver().removeOnPreDrawListener(this); - return true; + if (!messages.isEmpty()) { + if (messages.get(messages.size() - 1) == scrollToMessage) { + chatLayoutManager.scrollToPositionWithOffset(0, AndroidUtilities.dp(-11) + yOffset); + } else { + chatLayoutManager.scrollToPositionWithOffset(messages.size() - messages.indexOf(scrollToMessage), AndroidUtilities.dp(-11) + yOffset); } - }); + ViewTreeObserver obs = chatListView.getViewTreeObserver(); + obs.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { //TODO remove it? + if (!messages.isEmpty()) { + if (messages.get(messages.size() - 1) == scrollToMessage) { + chatLayoutManager.scrollToPositionWithOffset(0, AndroidUtilities.dp(-11) + yOffset); + } else { + chatLayoutManager.scrollToPositionWithOffset(messages.size() - messages.indexOf(scrollToMessage), AndroidUtilities.dp(-11) + yOffset); + } + } + chatListView.getViewTreeObserver().removeOnPreDrawListener(this); + return true; + } + }); + } chatListView.invalidate(); showPagedownButton(true, true); } else { moveScrollToLastMessage(); } } else { - int firstVisPos = chatListView.getLastVisiblePosition(); - View firstVisView = chatListView.getChildAt(chatListView.getChildCount() - 1); - int top = ((firstVisView == null) ? 0 : firstVisView.getTop()) - chatListView.getPaddingTop(); - chatAdapter.notifyDataSetChanged(); - chatListView.setSelectionFromTop(firstVisPos + newRowsCount - (endReached ? 1 : 0), top); + if (endReached) { + chatAdapter.notifyItemRemoved(0); + } + if (newRowsCount != 0) { + int firstVisPos = chatLayoutManager.findLastVisibleItemPosition(); + View firstVisView = chatListView.getChildAt(chatListView.getChildCount() - 1); + int top = ((firstVisView == null) ? 0 : firstVisView.getTop()) - chatListView.getPaddingTop(); + chatAdapter.notifyItemRangeInserted(1, newRowsCount); + chatLayoutManager.scrollToPositionWithOffset(firstVisPos + newRowsCount - (endReached ? 1 : 0), top); + } } if (paused) { @@ -3177,9 +3199,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } if (first) { - if (chatListView.getEmptyView() == null) { - chatListView.setEmptyView(emptyViewContainer); - } + chatListView.setEmptyView(emptyViewContainer); } } else { scrollToTopOnResume = true; @@ -3268,7 +3288,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null); builder.setMessage(LocaleController.formatString("CompatibilityChat", R.string.CompatibilityChat, currentUser.first_name, currentUser.first_name)); - showAlertDialog(builder); + showDialog(builder.create()); } } } @@ -3327,6 +3347,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } else { ReplyMessageQuery.loadReplyMessagesForMessages(arr, dialog_id); boolean markAsRead = false; + boolean unreadUpdated = true; int oldCount = messages.size(); for (MessageObject obj : arr) { if (currentEncryptedChat != null && obj.messageOwner.action != null && obj.messageOwner.action instanceof TLRPC.TL_messageEncryptedAction && @@ -3342,14 +3363,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } if (obj.isOut()) { - removeUnreadPlane(false); + removeUnreadPlane(); hasFromMe = true; } - if (!obj.isOut() && unreadMessageObject != null) { - unread_to_load++; - } - if (obj.getId() > 0) { maxMessageId = Math.min(obj.getId(), maxMessageId); minMessageId = Math.max(obj.getId(), minMessageId); @@ -3372,11 +3389,41 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not dateObj.contentType = 4; messages.add(0, dateObj); } - if (!obj.isOut() && obj.isUnread()) { - if (!paused) { - obj.setIsRead(); + if (!obj.isOut()) { + if (paused) { + if (!scrollToTopUnReadOnResume && unreadMessageObject != null) { + if (chatAdapter != null) { + chatAdapter.removeMessageObject(unreadMessageObject); + } else { + messages.remove(unreadMessageObject); + } + unreadMessageObject = null; + } + if (unreadMessageObject == null) { + TLRPC.Message dateMsg = new TLRPC.Message(); + dateMsg.message = ""; + dateMsg.id = 0; + MessageObject dateObj = new MessageObject(dateMsg, null, false); + dateObj.contentType = dateObj.type = 6; + messages.add(0, dateObj); + unreadMessageObject = dateObj; + scrollToMessage = unreadMessageObject; + scrollToMessageMiddleScreen = false; + unreadUpdated = false; + unread_to_load = 0; + scrollToTopUnReadOnResume = true; + } + } + if (unreadMessageObject != null) { + unread_to_load++; + unreadUpdated = true; + } + if (obj.isUnread()) { + if (!paused) { + obj.setIsRead(); + } + markAsRead = true; } - markAsRead = true; } dayArray.add(0, obj); messages.add(0, obj); @@ -3388,13 +3435,18 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not progressView.setVisibility(View.INVISIBLE); } if (chatAdapter != null) { - chatAdapter.notifyDataSetChanged(); + if (unreadUpdated) { + chatAdapter.updateRowWithMessageObject(unreadMessageObject); + } + if (messages.size() - oldCount != 0) { + chatAdapter.notifyItemRangeInserted(chatAdapter.getItemCount(), messages.size() - oldCount); + } } else { scrollToTopOnResume = true; } if (chatListView != null && chatAdapter != null) { - int lastVisible = chatListView.getLastVisiblePosition(); + int lastVisible = chatLayoutManager.findLastVisibleItemPosition(); if (endReached) { lastVisible++; } @@ -3516,7 +3568,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } if (updated && chatAdapter != null) { - removeUnreadPlane(false); + removeUnreadPlane(); chatAdapter.notifyDataSetChanged(); } } else if (id == NotificationCenter.messageReceivedByServer) { @@ -3535,9 +3587,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not obj.messageOwner.id = newMsgId; obj.messageOwner.send_state = MessageObject.MESSAGE_SEND_STATE_SENT; updateVisibleRows(); - if (mediaUpdated && chatListView.getLastVisiblePosition() >= messages.size() - 1) { + if (mediaUpdated && chatLayoutManager.findLastVisibleItemPosition() >= messages.size() - 1) { moveScrollToLastMessage(); } + NotificationsController.getInstance().playOutChatSound(); } } else if (id == NotificationCenter.messageReceivedByAck) { Integer msgId = (Integer) args[0]; @@ -3576,7 +3629,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not updateContactStatus(); updateSecretStatus(); } - } else if (id == NotificationCenter.messagesReadedEncrypted) { + } else if (id == NotificationCenter.messagesReadEncrypted) { int encId = (Integer) args[0]; if (currentEncryptedChat != null && currentEncryptedChat.id == encId) { int date = (Integer) args[1]; @@ -3709,13 +3762,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not int idx = messages.indexOf(old); if (idx >= 0) { messages.set(idx, messageObject); + chatAdapter.notifyItemChanged(messages.size() - (!endReached ? 0 : 1) - idx); changed = true; } } } if (changed) { - chatAdapter.notifyDataSetChanged(); - if (mediaUpdated && chatListView.getLastVisiblePosition() >= messages.size() - 1) { + if (mediaUpdated && chatLayoutManager.findLastVisibleItemPosition() >= messages.size() - 1) { moveScrollToLastMessage(); } } @@ -3740,7 +3793,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } if (updated) { updateVisibleRows(); - if (chatListView.getLastVisiblePosition() >= messages.size() - 1) { + if (chatLayoutManager.findLastVisibleItemPosition() >= messages.size() - 1) { moveScrollToLastMessage(); } } @@ -3754,11 +3807,30 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } } + } else if (id == NotificationCenter.messagesReadContent) { + ArrayList arrayList = (ArrayList) args[0]; + boolean updated = false; + for (Integer mid : arrayList) { + MessageObject currentMessage = messagesDict.get(mid); + if (currentMessage != null) { + currentMessage.setContentIsRead(); + updated = true; + } + } + if (updated) { + updateVisibleRows(); + } } } @Override - public void onOpenAnimationEnd() { + protected void onOpenAnimationStart() { + NotificationCenter.getInstance().setAnimationInProgress(true); + } + + @Override + protected void onOpenAnimationEnd() { + NotificationCenter.getInstance().setAnimationInProgress(false); openAnimationEnded = true; int count = chatListView.getChildCount(); for (int a = 0; a < count; a++) { @@ -3831,10 +3903,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not getParentActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); } - if (chatAdapter != null) { - chatAdapter.notifyDataSetChanged(); - } - checkActionBarMenu(); if (replyImageLocation != null && replyImageView != null) { replyImageView.setImage(replyImageLocation, "50_50", (Drawable) null); @@ -3845,7 +3913,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (scrollToTopUnReadOnResume && scrollToMessage != null) { if (chatListView != null) { final int yOffset = scrollToMessageMiddleScreen ? Math.max(0, (chatListView.getHeight() - scrollToMessage.getApproximateHeight()) / 2) : 0; - chatListView.setSelectionFromTop(messages.size() - messages.indexOf(scrollToMessage), -chatListView.getPaddingTop() - AndroidUtilities.dp(7) + yOffset); + chatLayoutManager.scrollToPositionWithOffset(messages.size() - messages.indexOf(scrollToMessage), -chatListView.getPaddingTop() - AndroidUtilities.dp(7) + yOffset); } } else { moveScrollToLastMessage(); @@ -3867,6 +3935,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not readWhenResume = false; MessagesController.getInstance().markDialogAsRead(dialog_id, messages.get(0).getId(), readWithMid, 0, readWithDate, true, false); } + if (wasPaused) { + wasPaused = false; + if (chatAdapter != null) { + chatAdapter.notifyDataSetChanged(); + } + } fixLayout(true); SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); @@ -3882,7 +3956,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not byte[] bytes = Base64.decode(lastReplyMessage, Base64.DEFAULT); if (bytes != null) { SerializedData data = new SerializedData(bytes); - TLRPC.Message message = (TLRPC.Message) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); if (message != null) { replyingMessageObject = new MessageObject(message, MessagesController.getInstance().getUsers(), false); showReplyPanel(true, replyingMessageObject, null, null, false, false); @@ -3904,7 +3978,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { - openVideoEditor(startVideoEdit, false); + openVideoEditor(startVideoEdit, false, false); startVideoEdit = null; } }); @@ -3928,6 +4002,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not menuItem.closeSubMenu(); } paused = true; + wasPaused = true; NotificationsController.getInstance().setOpennedDialogId(0); if (chatActivityEnterView != null) { chatActivityEnterView.hideEmojiPopup(); @@ -3957,7 +4032,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not editor.commit(); } - MessagesController.getInstance().cancelTyping(dialog_id); + MessagesController.getInstance().cancelTyping(0, dialog_id); if (currentEncryptedChat != null) { chatLeaveTime = System.currentTimeMillis(); @@ -4019,22 +4094,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } }); } - if (!resume && chatListView != null) { - final int lastPos = chatListView.getLastVisiblePosition(); - chatListView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - if (chatListView == null) { - return false; - } - chatListView.getViewTreeObserver().removeOnPreDrawListener(this); - if (lastPos >= messages.size() - 1) { - moveScrollToLastMessage(); - } - return false; - } - }); - } } @Override @@ -4064,7 +4123,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not selectedMessagesIds.clear(); actionBar.hideActionMode(); - if (single || type < 2 || type == 7) { + if (single || type < 2 || type == 20) { if (type >= 0) { selectedObject = message; if (getParentActivity() == null) { @@ -4086,7 +4145,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not items = new CharSequence[]{LocaleController.getString("Delete", R.string.Delete)}; options = new int[]{1}; } - } else if (type == 7) { + } else if (type == 20) { items = new CharSequence[]{LocaleController.getString("Retry", R.string.Retry), LocaleController.getString("Copy", R.string.Copy), LocaleController.getString("Delete", R.string.Delete)}; options = new int[]{0, 3, 1}; } else { @@ -4100,17 +4159,21 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not options = new int[]{8, 2, 3, 1}; } else if (type == 4) { if (selectedObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument) { - items = new CharSequence[]{LocaleController.getString("Reply", R.string.Reply), LocaleController.getString("ShareFile", R.string.ShareFile), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)}; + items = new CharSequence[]{LocaleController.getString("Reply", R.string.Reply), LocaleController.getString("SaveToDownloads", R.string.SaveToDownloads), LocaleController.getString("ShareFile", R.string.ShareFile), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)}; + options = new int[]{8, 10, 4, 2, 1}; } else { items = new CharSequence[]{LocaleController.getString("Reply", R.string.Reply), LocaleController.getString("SaveToGallery", R.string.SaveToGallery), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)}; + options = new int[]{8, 4, 2, 1}; } - options = new int[]{8, 4, 2, 1}; } else if (type == 5) { items = new CharSequence[]{LocaleController.getString("Reply", R.string.Reply), LocaleController.getString("ApplyLocalizationFile", R.string.ApplyLocalizationFile), LocaleController.getString("ShareFile", R.string.ShareFile), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)}; options = new int[]{8, 5, 4, 2, 1}; } else if (type == 6) { - items = new CharSequence[]{LocaleController.getString("Reply", R.string.Reply), LocaleController.getString("SaveToGallery", R.string.SaveToGallery), LocaleController.getString("ShareFile", R.string.ShareFile), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)}; - options = new int[]{8, 7, 6, 2, 1}; + items = new CharSequence[]{LocaleController.getString("Reply", R.string.Reply), LocaleController.getString("SaveToGallery", R.string.SaveToGallery), LocaleController.getString("SaveToDownloads", R.string.SaveToDownloads), LocaleController.getString("ShareFile", R.string.ShareFile), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)}; + options = new int[]{8, 7, 10, 6, 2, 1}; + } else if (type == 7) { + items = new CharSequence[]{LocaleController.getString("Reply", R.string.Reply), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("AddToStickers", R.string.AddToStickers), LocaleController.getString("Delete", R.string.Delete)}; + options = new int[]{8, 2, 9, 1}; } } else { if (type == 2) { @@ -4121,17 +4184,21 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not options = new int[]{2, 3, 1}; } else if (type == 4) { if (selectedObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument) { - items = new CharSequence[]{LocaleController.getString("ShareFile", R.string.ShareFile), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)}; + items = new CharSequence[]{LocaleController.getString("SaveToDownloads", R.string.SaveToDownloads), LocaleController.getString("ShareFile", R.string.ShareFile), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)}; + options = new int[]{10, 4, 2, 1}; } else { items = new CharSequence[]{LocaleController.getString("SaveToGallery", R.string.SaveToGallery), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)}; + options = new int[]{4, 2, 1}; } - options = new int[]{4, 2, 1}; } else if (type == 5) { items = new CharSequence[]{LocaleController.getString("ApplyLocalizationFile", R.string.ApplyLocalizationFile), LocaleController.getString("ShareFile", R.string.ShareFile), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)}; options = new int[]{5, 4, 2, 1}; } else if (type == 6) { - items = new CharSequence[]{LocaleController.getString("SaveToGallery", R.string.SaveToGallery), LocaleController.getString("ShareFile", R.string.ShareFile), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)}; - options = new int[]{7, 6, 2, 1}; + items = new CharSequence[]{LocaleController.getString("SaveToGallery", R.string.SaveToGallery), LocaleController.getString("SaveToDownloads", R.string.SaveToDownloads), LocaleController.getString("ShareFile", R.string.ShareFile), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)}; + options = new int[]{7, 10, 6, 2, 1}; + } else if (type == 7) { + items = new CharSequence[]{LocaleController.getString("Reply", R.string.Reply), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("AddToStickers", R.string.AddToStickers), LocaleController.getString("Delete", R.string.Delete)}; + options = new int[]{8, 2, 9, 1}; } } } else { @@ -4143,17 +4210,19 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not options = new int[]{3, 1}; } else if (type == 4) { if (selectedObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument) { - items = new CharSequence[]{LocaleController.getString("ShareFile", R.string.ShareFile), LocaleController.getString("Delete", R.string.Delete)}; + items = new CharSequence[]{LocaleController.getString("SaveToDownloads", R.string.SaveToDownloads), LocaleController.getString("ShareFile", R.string.ShareFile), LocaleController.getString("Delete", R.string.Delete)}; + options = new int[]{10, 4, 1}; } else { items = new CharSequence[]{LocaleController.getString("SaveToGallery", R.string.SaveToGallery), LocaleController.getString("Delete", R.string.Delete)}; + options = new int[]{4, 1}; } - options = new int[]{4, 1}; } else if (type == 5) { items = new CharSequence[]{LocaleController.getString("ApplyLocalizationFile", R.string.ApplyLocalizationFile), LocaleController.getString("Delete", R.string.Delete)}; options = new int[]{5, 1}; - }/* else if (type == 6) { - options = new int[]{7, 6, 2, 1}; - }*/ + } else if (type == 7) { + items = new CharSequence[]{LocaleController.getString("Reply", R.string.Reply), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("AddToStickers", R.string.AddToStickers), LocaleController.getString("Delete", R.string.Delete)}; + options = new int[]{8, 2, 9, 1}; + } } } @@ -4169,7 +4238,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not }); builder.setTitle(LocaleController.getString("Message", R.string.Message)); - showAlertDialog(builder); + showDialog(builder.create()); } return; } @@ -4215,7 +4284,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not public void onClick(DialogInterface dialogInterface, int i) { ArrayList ids = new ArrayList<>(); ids.add(finalSelectedObject.getId()); - removeUnreadPlane(true); + removeUnreadPlane(); ArrayList random_ids = null; if (currentEncryptedChat != null && finalSelectedObject.messageOwner.random_id != 0 && finalSelectedObject.type != 10) { random_ids = new ArrayList<>(); @@ -4225,7 +4294,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } else if (option == 2) { forwaringMessage = selectedObject; Bundle args = new Bundle(); @@ -4290,7 +4359,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); builder.setMessage(LocaleController.getString("IncorrectLocalization", R.string.IncorrectLocalization)); builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null); - showAlertDialog(builder); + showDialog(builder.create()); } } } else if (option == 6 || option == 7) { @@ -4317,77 +4386,31 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } else if (option == 8) { showReplyPanel(true, selectedObject, null, null, false, true); + } else if (option == 9) { + StickersQuery.loadStickers(this, selectedObject.getInputStickerSet()); + } else if (option == 10) { + String fileName = FileLoader.getDocumentFileName(selectedObject.messageOwner.media.document); + if (fileName == null || fileName.length() == 0) { + fileName = selectedObject.getFileName(); + } + String path = selectedObject.messageOwner.attachPath; + if (path != null && path.length() > 0) { + File temp = new File(path); + if (!temp.exists()) { + path = null; + } + } + if (path == null || path.length() == 0) { + path = FileLoader.getPathToMessage(selectedObject.messageOwner).toString(); + } + MediaController.saveFile(path, getParentActivity(), 2, fileName); } selectedObject = null; } - private void openProfileWithUsername(String username) { - TLRPC.User user = MessagesController.getInstance().getUser(username); - if (user != null) { - Bundle args = new Bundle(); - args.putInt("user_id", user.id); - presentFragment(new ProfileActivity(args)); - } else { - if (getParentActivity() == null) { - return; - } - final ProgressDialog progressDialog = new ProgressDialog(getParentActivity()); - progressDialog.setMessage(LocaleController.getString("Loading", R.string.Loading)); - progressDialog.setCanceledOnTouchOutside(false); - progressDialog.setCancelable(false); - - TLRPC.TL_contacts_resolveUsername req = new TLRPC.TL_contacts_resolveUsername(); - req.username = username; - final long 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() { - try { - progressDialog.dismiss(); - } catch (Exception e) { - FileLog.e("tmessages", e); - } - visibleDialog = null; - if (error == null) { - TLRPC.User user = (TLRPC.User) response; - MessagesController.getInstance().putUser(user, false); - ArrayList users = new ArrayList<>(); - users.add(user); - MessagesStorage.getInstance().putUsersAndChats(users, null, false, true); - Bundle args = new Bundle(); - args.putInt("user_id", user.id); - presentFragment(new ProfileActivity(args)); - } - } - }); - } - }); - progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, LocaleController.getString("Cancel", R.string.Cancel), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - ConnectionsManager.getInstance().cancelRpc(reqId, true); - try { - dialog.dismiss(); - } catch (Exception e) { - FileLog.e("tmessages", e); - } - visibleDialog = null; - } - }); - visibleDialog = progressDialog; - progressDialog.show(); - } - } - @Override public void didSelectDialog(MessagesActivity activity, long did, boolean param) { if (dialog_id != 0 && (forwaringMessage != null || !selectedMessagesIds.isEmpty())) { - if (isBroadcast) { - param = true; - } - ArrayList fmessages = new ArrayList<>(); if (forwaringMessage != null) { fmessages.add(forwaringMessage); @@ -4442,6 +4465,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (AndroidUtilities.isTablet()) { actionBar.hideActionMode(); } + updateVisibleRows(); } } } @@ -4484,7 +4508,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); return false; } } @@ -4532,7 +4556,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } else { builder.setMessage(LocaleController.formatString("NoHandleAppInstalled", R.string.NoHandleAppInstalled, message.messageOwner.media.document.mime_type)); } - showAlertDialog(builder); + showDialog(builder.create()); } @Override @@ -4618,26 +4642,23 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not return 0; } - private class ChatAdapter extends BaseFragmentAdapter { + public class ChatActivityAdapter extends RecyclerView.Adapter { private Context mContext; - public ChatAdapter(Context context) { + public ChatActivityAdapter(Context context) { mContext = context; } - @Override - public boolean areAllItemsEnabled() { - return true; + private class Holder extends RecyclerView.ViewHolder { + + public Holder(View itemView) { + super(itemView); + } } @Override - public boolean isEnabled(int i) { - return true; - } - - @Override - public int getCount() { + public int getItemCount() { int count = messages.size(); if (count != 0) { if (!endReached) { @@ -4650,290 +4671,290 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not return count; } - @Override - public Object getItem(int i) { - return null; - } - @Override public long getItemId(int i) { - return i; + return RecyclerListView.NO_ID; } @Override - public boolean hasStableIds() { - return true; - } - - @Override - public View getView(int i, View view, ViewGroup viewGroup) { - int offset = 1; - if ((!endReached || !forward_end_reached) && messages.size() != 0) { - if (!endReached) { - offset = 0; - } - if (i == 0 && !endReached || !forward_end_reached && i == (messages.size() + 1 - offset)) { - View progressBar = null; - if (view == null) { - LayoutInflater li = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - view = li.inflate(R.layout.chat_loading_layout, viewGroup, false); - progressBar = view.findViewById(R.id.progressLayout); - if (ApplicationLoader.isCustomTheme()) { - progressBar.setBackgroundResource(R.drawable.system_loader2); - } else { - progressBar.setBackgroundResource(R.drawable.system_loader1); - } - } else { - progressBar = view.findViewById(R.id.progressLayout); - } - progressBar.setVisibility(loadsCount > 1 ? View.VISIBLE : View.INVISIBLE); - - return view; - } - } - final MessageObject message = messages.get(messages.size() - i - offset); - int type = message.contentType; - if (view == null) { - if (type == 0) { + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = null; + if (viewType == 0) { + if (!chatMessageCellsCache.isEmpty()) { + view = chatMessageCellsCache.get(0); + chatMessageCellsCache.remove(0); + } else { view = new ChatMessageCell(mContext); } - if (type == 1) { + } else if (viewType == 1) { + if (!chatMediaCellsCache.isEmpty()) { + view = chatMediaCellsCache.get(0); + chatMediaCellsCache.remove(0); + } else { view = new ChatMediaCell(mContext); - } else if (type == 2) { - view = new ChatAudioCell(mContext); - } else if (type == 3) { - view = new ChatContactCell(mContext); - } else if (type == 6) { - LayoutInflater li = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - view = li.inflate(R.layout.chat_unread_layout, viewGroup, false); - } else if (type == 4) { - view = new ChatActionCell(mContext); } + } else if (viewType == 2) { + view = new ChatAudioCell(mContext); + } else if (viewType == 3) { + view = new ChatContactCell(mContext); + } else if (viewType == 4) { + view = new ChatActionCell(mContext); + } else if (viewType == 5) { + LayoutInflater li = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = li.inflate(R.layout.chat_loading_layout, parent, false); + view.findViewById(R.id.progressLayout).setBackgroundResource(ApplicationLoader.isCustomTheme() ? R.drawable.system_loader2 : R.drawable.system_loader1); + } else if (viewType == 6) { + LayoutInflater li = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = li.inflate(R.layout.chat_unread_layout, parent, false); + } - if (view instanceof ChatBaseCell) { - ((ChatBaseCell) view).setDelegate(new ChatBaseCell.ChatBaseCellDelegate() { + if (view instanceof ChatBaseCell) { + ((ChatBaseCell) view).setDelegate(new ChatBaseCell.ChatBaseCellDelegate() { + @Override + public void didPressedUserAvatar(ChatBaseCell cell, TLRPC.User user) { + if (actionBar.isActionModeShowed()) { + processRowSelect(cell); + return; + } + if (user != null && user.id != UserConfig.getClientUserId()) { + Bundle args = new Bundle(); + args.putInt("user_id", user.id); + presentFragment(new ProfileActivity(args)); + } + } + + @Override + public void didPressedCancelSendButton(ChatBaseCell cell) { + MessageObject message = cell.getMessageObject(); + if (message.messageOwner.send_state != 0) { + SendMessagesHelper.getInstance().cancelSendingMessage(message); + } + } + + @Override + public void didLongPressed(ChatBaseCell cell) { + createMenu(cell, false); + } + + @Override + public boolean canPerformActions() { + return actionBar != null && !actionBar.isActionModeShowed(); + } + + @Override + public void didPressUrl(String url) { + if (url.startsWith("@")) { + MessagesController.openByUserName(url.substring(1), ChatActivity.this, 0); + } else if (url.startsWith("#")) { + MessagesActivity fragment = new MessagesActivity(null); + fragment.setSearchString(url); + presentFragment(fragment); + } + } + + @Override + public void needOpenWebView(String url, String title, String originalUrl, int w, int h) { + BottomSheet.Builder builder = new BottomSheet.Builder(mContext); + builder.setCustomView(new WebFrameLayout(mContext, builder.create(), title, originalUrl, url, w, h)); + builder.setOverrideTabletWidth(true); + showDialog(builder.create()); + } + + @Override + public void didPressReplyMessage(ChatBaseCell cell, int id) { + scrollToMessageId(id, cell.getMessageObject().getId(), true); + } + }); + if (view instanceof ChatMediaCell) { + ((ChatMediaCell) view).setAllowedToSetPhoto(openAnimationEnded); + ((ChatMediaCell) view).setMediaDelegate(new ChatMediaCell.ChatMediaCellDelegate() { @Override - public void didPressedUserAvatar(ChatBaseCell cell, TLRPC.User user) { - if (actionBar.isActionModeShowed()) { - processRowSelect(cell); + public void didClickedImage(ChatMediaCell cell) { + MessageObject message = cell.getMessageObject(); + if (message.isSendError()) { + createMenu(cell, false); + return; + } else if (message.isSending()) { return; } - if (user != null && user.id != UserConfig.getClientUserId()) { - Bundle args = new Bundle(); - args.putInt("user_id", user.id); - presentFragment(new ProfileActivity(args)); - } - } - - @Override - public void didPressedCancelSendButton(ChatBaseCell cell) { - MessageObject message = cell.getMessageObject(); - if (message.messageOwner.send_state != 0) { - SendMessagesHelper.getInstance().cancelSendingMessage(message); - } - } - - @Override - public void didLongPressed(ChatBaseCell cell) { - createMenu(cell, false); - } - - @Override - public boolean canPerformActions() { - return actionBar != null && !actionBar.isActionModeShowed(); - } - - @Override - public void didPressUrl(String url) { - if (url.startsWith("@")) { - openProfileWithUsername(url.substring(1)); - } else if (url.startsWith("#")) { - MessagesActivity fragment = new MessagesActivity(null); - fragment.setSearchString(url); - presentFragment(fragment); - } - } - - @Override - public void didPressReplyMessage(ChatBaseCell cell, int id) { - scrollToMessageId(id, cell.getMessageObject().getId(), true); - } - }); - if (view instanceof ChatMediaCell) { - ((ChatMediaCell) view).setAllowedToSetPhoto(openAnimationEnded); - ((ChatMediaCell) view).setMediaDelegate(new ChatMediaCell.ChatMediaCellDelegate() { - @Override - public void didClickedImage(ChatMediaCell cell) { - MessageObject message = cell.getMessageObject(); - if (message.isSendError()) { - createMenu(cell, false); - return; - } else if (message.isSending()) { - return; - } - if (message.type == 1) { - PhotoViewer.getInstance().setParentActivity(getParentActivity()); - PhotoViewer.getInstance().openPhoto(message, ChatActivity.this); - } else if (message.type == 3) { - sendSecretMessageRead(message); - try { - File f = null; - if (message.messageOwner.attachPath != null && message.messageOwner.attachPath.length() != 0) { - f = new File(message.messageOwner.attachPath); - } - if (f == null || f != null && !f.exists()) { - f = FileLoader.getPathToMessage(message.messageOwner); - } - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setDataAndType(Uri.fromFile(f), "video/mp4"); - getParentActivity().startActivityForResult(intent, 500); - } catch (Exception e) { - alertUserOpenError(message); - } - } else if (message.type == 4) { - if (!isGoogleMapsInstalled()) { - return; - } - LocationActivity fragment = new LocationActivity(); - fragment.setMessageObject(message); - presentFragment(fragment); - } else if (message.type == 9) { + if (message.type == 1) { + PhotoViewer.getInstance().setParentActivity(getParentActivity()); + PhotoViewer.getInstance().openPhoto(message, ChatActivity.this); + } else if (message.type == 3) { + sendSecretMessageRead(message); + try { File f = null; - String fileName = message.getFileName(); if (message.messageOwner.attachPath != null && message.messageOwner.attachPath.length() != 0) { f = new File(message.messageOwner.attachPath); } if (f == null || f != null && !f.exists()) { f = FileLoader.getPathToMessage(message.messageOwner); } - if (f != null && f.exists()) { - String realMimeType = null; - try { - Intent intent = new Intent(Intent.ACTION_VIEW); - if (message.type == 8 || message.type == 9) { - MimeTypeMap myMime = MimeTypeMap.getSingleton(); - int idx = fileName.lastIndexOf("."); - if (idx != -1) { - String ext = fileName.substring(idx + 1); - realMimeType = myMime.getMimeTypeFromExtension(ext.toLowerCase()); - if (realMimeType == null) { - realMimeType = message.messageOwner.media.document.mime_type; - if (realMimeType == null || realMimeType.length() == 0) { - realMimeType = null; - } - } - if (realMimeType != null) { - intent.setDataAndType(Uri.fromFile(f), realMimeType); - } else { - intent.setDataAndType(Uri.fromFile(f), "text/plain"); + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setDataAndType(Uri.fromFile(f), "video/mp4"); + getParentActivity().startActivityForResult(intent, 500); + } catch (Exception e) { + alertUserOpenError(message); + } + } else if (message.type == 4) { + if (!isGoogleMapsInstalled()) { + return; + } + LocationActivity fragment = new LocationActivity(); + fragment.setMessageObject(message); + presentFragment(fragment); + } else if (message.type == 9) { + File f = null; + String fileName = message.getFileName(); + if (message.messageOwner.attachPath != null && message.messageOwner.attachPath.length() != 0) { + f = new File(message.messageOwner.attachPath); + } + if (f == null || f != null && !f.exists()) { + f = FileLoader.getPathToMessage(message.messageOwner); + } + if (f != null && f.exists()) { + String realMimeType = null; + try { + Intent intent = new Intent(Intent.ACTION_VIEW); + if (message.type == 8 || message.type == 9) { + MimeTypeMap myMime = MimeTypeMap.getSingleton(); + int idx = fileName.lastIndexOf("."); + if (idx != -1) { + String ext = fileName.substring(idx + 1); + realMimeType = myMime.getMimeTypeFromExtension(ext.toLowerCase()); + if (realMimeType == null) { + realMimeType = message.messageOwner.media.document.mime_type; + if (realMimeType == null || realMimeType.length() == 0) { + realMimeType = null; } + } + if (realMimeType != null) { + intent.setDataAndType(Uri.fromFile(f), realMimeType); } else { intent.setDataAndType(Uri.fromFile(f), "text/plain"); } - } - if (realMimeType != null) { - try { - getParentActivity().startActivityForResult(intent, 500); - } catch (Exception e) { - intent.setDataAndType(Uri.fromFile(f), "text/plain"); - getParentActivity().startActivityForResult(intent, 500); - } } else { + intent.setDataAndType(Uri.fromFile(f), "text/plain"); + } + } + if (realMimeType != null) { + try { + getParentActivity().startActivityForResult(intent, 500); + } catch (Exception e) { + intent.setDataAndType(Uri.fromFile(f), "text/plain"); getParentActivity().startActivityForResult(intent, 500); } - } catch (Exception e) { - alertUserOpenError(message); + } else { + getParentActivity().startActivityForResult(intent, 500); } + } catch (Exception e) { + alertUserOpenError(message); } } } + } - @Override - public void didPressedOther(ChatMediaCell cell) { - createMenu(cell, true); - } - }); - } else if (view instanceof ChatContactCell) { - ((ChatContactCell) view).setContactDelegate(new ChatContactCell.ChatContactCellDelegate() { - @Override - public void didClickAddButton(ChatContactCell cell, TLRPC.User user) { - if (actionBar.isActionModeShowed()) { - processRowSelect(cell); - return; - } - MessageObject messageObject = cell.getMessageObject(); - Bundle args = new Bundle(); - args.putInt("user_id", messageObject.messageOwner.media.user_id); - args.putString("phone", messageObject.messageOwner.media.phone_number); - args.putBoolean("addContact", true); - presentFragment(new ContactAddActivity(args)); + @Override + public void didPressedOther(ChatMediaCell cell) { + createMenu(cell, true); + } + }); + } else if (view instanceof ChatContactCell) { + ((ChatContactCell) view).setContactDelegate(new ChatContactCell.ChatContactCellDelegate() { + @Override + public void didClickAddButton(ChatContactCell cell, TLRPC.User user) { + if (actionBar.isActionModeShowed()) { + processRowSelect(cell); + return; } + MessageObject messageObject = cell.getMessageObject(); + Bundle args = new Bundle(); + args.putInt("user_id", messageObject.messageOwner.media.user_id); + args.putString("phone", messageObject.messageOwner.media.phone_number); + args.putBoolean("addContact", true); + presentFragment(new ContactAddActivity(args)); + } - @Override - public void didClickPhone(ChatContactCell cell) { - if (actionBar.isActionModeShowed()) { - processRowSelect(cell); - return; - } - final MessageObject messageObject = cell.getMessageObject(); - if (getParentActivity() == null || messageObject.messageOwner.media.phone_number == null || messageObject.messageOwner.media.phone_number.length() == 0) { - return; - } - AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); - builder.setItems(new CharSequence[]{LocaleController.getString("Copy", R.string.Copy), LocaleController.getString("Call", R.string.Call)}, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - if (i == 1) { - try { - Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + messageObject.messageOwner.media.phone_number)); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - getParentActivity().startActivityForResult(intent, 500); - } catch (Exception e) { - FileLog.e("tmessages", e); - } - } else if (i == 0) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { - android.text.ClipboardManager clipboard = (android.text.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE); - clipboard.setText(messageObject.messageOwner.media.phone_number); - } else { - android.content.ClipboardManager clipboard = (android.content.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE); - android.content.ClipData clip = android.content.ClipData.newPlainText("label", messageObject.messageOwner.media.phone_number); - clipboard.setPrimaryClip(clip); - } + @Override + public void didClickPhone(ChatContactCell cell) { + if (actionBar.isActionModeShowed()) { + processRowSelect(cell); + return; + } + final MessageObject messageObject = cell.getMessageObject(); + if (getParentActivity() == null || messageObject.messageOwner.media.phone_number == null || messageObject.messageOwner.media.phone_number.length() == 0) { + return; + } + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + builder.setItems(new CharSequence[]{LocaleController.getString("Copy", R.string.Copy), LocaleController.getString("Call", R.string.Call)}, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + if (i == 1) { + try { + Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + messageObject.messageOwner.media.phone_number)); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + getParentActivity().startActivityForResult(intent, 500); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } else if (i == 0) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { + android.text.ClipboardManager clipboard = (android.text.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE); + clipboard.setText(messageObject.messageOwner.media.phone_number); + } else { + android.content.ClipboardManager clipboard = (android.content.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE); + android.content.ClipData clip = android.content.ClipData.newPlainText("label", messageObject.messageOwner.media.phone_number); + clipboard.setPrimaryClip(clip); } } } - ); - showAlertDialog(builder); - } - }); - } - } else if (view instanceof ChatActionCell) { - ((ChatActionCell) view).setDelegate(new ChatActionCell.ChatActionCellDelegate() { - @Override - public void didClickedImage(ChatActionCell cell) { - MessageObject message = cell.getMessageObject(); - PhotoViewer.getInstance().setParentActivity(getParentActivity()); - PhotoViewer.getInstance().openPhoto(message, ChatActivity.this); - } - - @Override - public void didLongPressed(ChatActionCell cell) { - createMenu(cell, false); - } - - @Override - public void needOpenUserProfile(int uid) { - if (uid != UserConfig.getClientUserId()) { - Bundle args = new Bundle(); - args.putInt("user_id", uid); - presentFragment(new ProfileActivity(args)); - } + } + ); + showDialog(builder.create()); } }); } + } else if (view instanceof ChatActionCell) { + ((ChatActionCell) view).setDelegate(new ChatActionCell.ChatActionCellDelegate() { + @Override + public void didClickedImage(ChatActionCell cell) { + MessageObject message = cell.getMessageObject(); + PhotoViewer.getInstance().setParentActivity(getParentActivity()); + PhotoViewer.getInstance().openPhoto(message, ChatActivity.this); + } + + @Override + public void didLongPressed(ChatActionCell cell) { + createMenu(cell, false); + } + + @Override + public void needOpenUserProfile(int uid) { + if (uid != UserConfig.getClientUserId()) { + Bundle args = new Bundle(); + args.putInt("user_id", uid); + presentFragment(new ProfileActivity(args)); + } + } + }); } + return new Holder(view); + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + int viewType = holder.getItemViewType(); + if (viewType == 5) { + holder.itemView.findViewById(R.id.progressLayout).setVisibility(loadsCount > 1 ? View.VISIBLE : View.INVISIBLE); + return; + } + + MessageObject message = messages.get(messages.size() - position - (!endReached ? 0 : 1)); + View view = holder.itemView; + + int type = message.contentType; + boolean selected = false; boolean disableSelection = false; if (actionBar.isActionModeShowed()) { @@ -4960,48 +4981,63 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } else if (view instanceof ChatActionCell) { ChatActionCell actionCell = (ChatActionCell) view; actionCell.setMessageObject(message); - } - if (type == 6) { + } else if (type == 6) { TextView messageTextView = (TextView) view.findViewById(R.id.chat_message_text); messageTextView.setText(LocaleController.formatPluralString("NewMessages", unread_to_load)); } - - return view; } @Override - public int getItemViewType(int i) { + public int getItemViewType(int position) { int offset = 1; if (!endReached && messages.size() != 0) { offset = 0; - if (i == 0) { + if (position == 0) { return 5; } } - if (!forward_end_reached && i == (messages.size() + 1 - offset)) { + if (!forward_end_reached && position == (messages.size() + 1 - offset)) { return 5; } - MessageObject message = messages.get(messages.size() - i - offset); + MessageObject message = messages.get(messages.size() - position - offset); return message.contentType; } @Override - public int getViewTypeCount() { - return 7; + public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { + if (holder.itemView instanceof ChatBaseCell) { + ChatBaseCell baseCell = (ChatBaseCell) holder.itemView; + baseCell.setHighlighted(highlightMessageId != Integer.MAX_VALUE && baseCell.getMessageObject().getId() == highlightMessageId); + } + if (holder.itemView instanceof ChatMessageCell) { + final ChatMessageCell messageCell = (ChatMessageCell) holder.itemView; + messageCell.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + messageCell.getViewTreeObserver().removeOnPreDrawListener(this); + messageCell.getLocalVisibleRect(scrollRect); + messageCell.setVisiblePart(scrollRect.top, scrollRect.bottom - scrollRect.top); + return true; + } + }); + } } - @Override - public boolean isEmpty() { - int count = messages.size(); - if (count != 0) { - if (!endReached) { - count++; - } - if (!forward_end_reached) { - count++; - } + public void updateRowWithMessageObject(MessageObject messageObject) { + int index = messages.indexOf(messageObject); + if (index == -1) { + return; } - return count == 0; + notifyItemChanged(messages.size() - (!endReached ? 0 : 1) - index); + } + + public void removeMessageObject(MessageObject messageObject) { + int index = messages.indexOf(messageObject); + if (index == -1) { + return; + } + messages.remove(index); + notifyItemRemoved(messages.size() - (!endReached ? 0 : 1) - index); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarDrawable.java index e2a850391..e35f5f3ce 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarDrawable.java @@ -52,6 +52,7 @@ public class AvatarDrawable extends Drawable { private boolean drawBrodcast; private boolean drawPhoto; private boolean smallStyle; + private StringBuilder stringBuilder = new StringBuilder(5); public AvatarDrawable() { super(); @@ -178,9 +179,9 @@ public class AvatarDrawable extends Drawable { lastName = null; } - String text = ""; + stringBuilder.setLength(0); if (firstName != null && firstName.length() > 0) { - text += firstName.substring(0, 1); + stringBuilder.append(firstName.substring(0, 1)); } if (lastName != null && lastName.length() > 0) { String lastch = null; @@ -191,27 +192,25 @@ public class AvatarDrawable extends Drawable { lastch = lastName.substring(a, a + 1); } if (Build.VERSION.SDK_INT >= 16) { - text += "\u200C" + lastch; - } else { - text += lastch; + stringBuilder.append("\u200C"); } + stringBuilder.append(lastch); } else if (firstName != null && firstName.length() > 0) { for (int a = firstName.length() - 1; a >= 0; a--) { if (firstName.charAt(a) == ' ') { if (a != firstName.length() - 1 && firstName.charAt(a + 1) != ' ') { if (Build.VERSION.SDK_INT >= 16) { - text += "\u200C" + firstName.substring(a + 1, a + 2); - } else { - text += firstName.substring(a + 1, a + 2); + stringBuilder.append("\u200C"); } + stringBuilder.append(firstName.substring(a + 1, a + 2)); break; } } } } - if (text.length() > 0) { - text = text.toUpperCase(); + if (stringBuilder.length() > 0) { + String text = stringBuilder.toString().toUpperCase(); try { textLayout = new StaticLayout(text, (smallStyle ? namePaintSmall : namePaint), AndroidUtilities.dp(100), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); if (textLayout.getLineCount() > 0) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarUpdater.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarUpdater.java index 1fa82ddd5..57e0b98a5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarUpdater.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarUpdater.java @@ -24,7 +24,6 @@ import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; import org.telegram.android.NotificationCenter; import org.telegram.messenger.UserConfig; -import org.telegram.messenger.Utilities; import org.telegram.ui.LaunchActivity; import org.telegram.ui.PhotoAlbumPickerActivity; import org.telegram.ui.PhotoCropActivity; @@ -62,7 +61,7 @@ public class AvatarUpdater implements NotificationCenter.NotificationCenterDeleg public void openCamera() { try { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - File image = Utilities.generatePicturePath(); + File image = AndroidUtilities.generatePicturePath(); if (image != null) { takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(image)); currentPicturePath = image.getAbsolutePath(); @@ -74,10 +73,10 @@ public class AvatarUpdater implements NotificationCenter.NotificationCenterDeleg } public void openGallery() { - PhotoAlbumPickerActivity fragment = new PhotoAlbumPickerActivity(true); + PhotoAlbumPickerActivity fragment = new PhotoAlbumPickerActivity(true, null); fragment.setDelegate(new PhotoAlbumPickerActivity.PhotoAlbumPickerActivityDelegate() { @Override - public void didSelectPhotos(ArrayList photos, ArrayList webPhotos) { + public void didSelectPhotos(ArrayList photos, ArrayList captions, ArrayList webPhotos) { if (!photos.isEmpty()) { Bitmap bitmap = ImageLoader.loadBitmap(photos.get(0), null, 800, 800, true); processBitmap(bitmap); @@ -94,6 +93,11 @@ public class AvatarUpdater implements NotificationCenter.NotificationCenterDeleg FileLog.e("tmessages", e); } } + + @Override + public boolean didSelectVideo(String path) { + return true; + } }); parentFragment.presentFragment(fragment); } @@ -143,7 +147,7 @@ public class AvatarUpdater implements NotificationCenter.NotificationCenterDeleg FileLog.e("tmessages", e); } final ArrayList arrayList = new ArrayList<>(); - arrayList.add(new MediaController.PhotoEntry(0, 0, 0, currentPicturePath, orientation)); + arrayList.add(new MediaController.PhotoEntry(0, 0, 0, currentPicturePath, orientation, false)); PhotoViewer.getInstance().openPhotoForSelect(arrayList, 0, 1, new PhotoViewer.EmptyPhotoViewerProvider() { @Override public void sendButtonPressed(int index) { @@ -157,8 +161,8 @@ public class AvatarUpdater implements NotificationCenter.NotificationCenterDeleg Bitmap bitmap = ImageLoader.loadBitmap(path, null, 800, 800, true); processBitmap(bitmap); } - }); - Utilities.addMediaToGallery(currentPicturePath); + }, null); + AndroidUtilities.addMediaToGallery(currentPicturePath); currentPicturePath = null; } else if (requestCode == 14) { if (data == null || data.getData() == null) { @@ -200,37 +204,27 @@ public class AvatarUpdater implements NotificationCenter.NotificationCenterDeleg if (id == NotificationCenter.FileDidUpload) { String location = (String)args[0]; if (uploadingAvatar != null && location.equals(uploadingAvatar)) { - AndroidUtilities.runOnUIThread(new Runnable() { - @Override - public void run() { - NotificationCenter.getInstance().removeObserver(AvatarUpdater.this, NotificationCenter.FileDidUpload); - NotificationCenter.getInstance().removeObserver(AvatarUpdater.this, NotificationCenter.FileDidFailUpload); - if (delegate != null) { - delegate.didUploadedPhoto((TLRPC.InputFile)args[1], smallPhoto, bigPhoto); - } - uploadingAvatar = null; - if (clearAfterUpdate) { - parentFragment = null; - delegate = null; - } - } - }); + NotificationCenter.getInstance().removeObserver(AvatarUpdater.this, NotificationCenter.FileDidUpload); + NotificationCenter.getInstance().removeObserver(AvatarUpdater.this, NotificationCenter.FileDidFailUpload); + if (delegate != null) { + delegate.didUploadedPhoto((TLRPC.InputFile)args[1], smallPhoto, bigPhoto); + } + uploadingAvatar = null; + if (clearAfterUpdate) { + parentFragment = null; + delegate = null; + } } } else if (id == NotificationCenter.FileDidFailUpload) { String location = (String)args[0]; if (uploadingAvatar != null && location.equals(uploadingAvatar)) { - AndroidUtilities.runOnUIThread(new Runnable() { - @Override - public void run() { - NotificationCenter.getInstance().removeObserver(AvatarUpdater.this, NotificationCenter.FileDidUpload); - NotificationCenter.getInstance().removeObserver(AvatarUpdater.this, NotificationCenter.FileDidFailUpload); - uploadingAvatar = null; - if (clearAfterUpdate) { - parentFragment = null; - delegate = null; - } - } - }); + NotificationCenter.getInstance().removeObserver(AvatarUpdater.this, NotificationCenter.FileDidUpload); + NotificationCenter.getInstance().removeObserver(AvatarUpdater.this, NotificationCenter.FileDidFailUpload); + uploadingAvatar = null; + if (clearAfterUpdate) { + parentFragment = null; + delegate = null; + } } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BackupImageView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BackupImageView.java index e625402ef..5fa640c07 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BackupImageView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BackupImageView.java @@ -23,6 +23,8 @@ import org.telegram.messenger.TLRPC; public class BackupImageView extends View { private ImageReceiver imageReceiver; + private int width = -1; + private int height = -1; public BackupImageView(Context context) { super(context); @@ -43,39 +45,43 @@ public class BackupImageView extends View { imageReceiver = new ImageReceiver(this); } + public void setImage(TLObject path, String filter, String ext, Drawable thumb) { + setImage(path, null, filter, thumb, null, null, null, ext, 0); + } + public void setImage(TLObject path, String filter, Drawable thumb) { - setImage(path, null, filter, thumb, null, null, null, 0); + setImage(path, null, filter, thumb, null, null, null, null, 0); } public void setImage(TLObject path, String filter, Bitmap thumb) { - setImage(path, null, filter, null, thumb, null, null, 0); + setImage(path, null, filter, null, thumb, null, null, null, 0); } public void setImage(TLObject path, String filter, Drawable thumb, int size) { - setImage(path, null, filter, thumb, null, null, null, size); + setImage(path, null, filter, thumb, null, null, null, null, size); } public void setImage(TLObject path, String filter, Bitmap thumb, int size) { - setImage(path, null, filter, null, thumb, null, null, size); + setImage(path, null, filter, null, thumb, null, null, null, size); } public void setImage(TLObject path, String filter, TLRPC.FileLocation thumb, int size) { - setImage(path, null, filter, null, null, thumb, null, size); + setImage(path, null, filter, null, null, thumb, null, null, size); } public void setImage(String path, String filter, Drawable thumb) { - setImage(null, path, filter, thumb, null, null, null, 0); + setImage(null, path, filter, thumb, null, null, null, null, 0); } public void setOrientation(int angle, boolean center) { imageReceiver.setOrientation(angle, center); } - public void setImage(TLObject path, String httpUrl, String filter, Drawable thumb, Bitmap thumbBitmap, TLRPC.FileLocation thumbLocation, String thumbFilter, int size) { + public void setImage(TLObject path, String httpUrl, String filter, Drawable thumb, Bitmap thumbBitmap, TLRPC.FileLocation thumbLocation, String thumbFilter, String ext, int size) { if (thumbBitmap != null) { thumb = new BitmapDrawable(null, thumbBitmap); } - imageReceiver.setImage(path, httpUrl, filter, thumb, thumbLocation, thumbFilter, size, false); + imageReceiver.setImage(path, httpUrl, filter, thumb, thumbLocation, thumbFilter, size, ext, false); } public void setImageBitmap(Bitmap bitmap) { @@ -102,15 +108,30 @@ public class BackupImageView extends View { return imageReceiver; } + public void setSize(int w, int h) { + width = w; + height = h; + } + @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - imageReceiver.clearImage(); + imageReceiver.onDetachedFromWindow(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + imageReceiver.onAttachedToWindow(); } @Override protected void onDraw(Canvas canvas) { - imageReceiver.setImageCoords(0, 0, getWidth(), getHeight()); + if (width != -1 && height != -1) { + imageReceiver.setImageCoords((getWidth() - width) / 2, (getHeight() - height) / 2, width, height); + } else { + imageReceiver.setImageCoords(0, 0, getWidth(), getHeight()); + } imageReceiver.draw(canvas); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java index de1b773b6..4d6580a2e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java @@ -12,7 +12,6 @@ import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; import android.media.AudioManager; -import android.os.Build; import android.os.PowerManager; import android.text.Editable; import android.text.TextWatcher; @@ -23,6 +22,7 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.ViewTreeObserver; import android.view.WindowManager; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.inputmethod.EditorInfo; @@ -31,7 +31,6 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.PopupWindow; -import android.widget.RelativeLayout; import android.widget.TextView; import org.telegram.android.AndroidUtilities; @@ -48,14 +47,12 @@ import org.telegram.messenger.R; import org.telegram.messenger.TLRPC; import org.telegram.messenger.UserConfig; import org.telegram.ui.ActionBar.BaseFragment; -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 org.telegram.android.AnimationCompat.AnimatorListenerAdapterProxy; +import org.telegram.android.AnimationCompat.AnimatorSetProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.ViewProxy; import org.telegram.messenger.ApplicationLoader; -import java.lang.reflect.Field; - public class ChatActivityEnterView extends FrameLayoutFixed implements NotificationCenter.NotificationCenterDelegate, SizeNotifierRelativeLayout.SizeNotifierRelativeLayoutDelegate { public interface ChatActivityEnterViewDelegate { @@ -76,11 +73,17 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat private ImageView audioSendButton; private FrameLayout recordPanel; private LinearLayout slideText; - private SizeNotifierRelativeLayout sizeNotifierRelativeLayout; + private View sizeNotifierLayout; private FrameLayout attachButton; private LinearLayout textFieldContainer; private View topView; + private int framesDroped; + + private int keyboardTransitionState; + private boolean showKeyboardOnEmojiButton; + private ViewTreeObserver.OnPreDrawListener onPreDrawListener; + private PowerManager.WakeLock mWakeLock; private AnimatorSetProxy runningAnimation; private AnimatorSetProxy runningAnimation2; @@ -98,6 +101,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat private float distCanMove = AndroidUtilities.dp(80); private boolean recordingAudio; private boolean forceShowSendButton; + private boolean allowStickers; private Activity parentActivity; private BaseFragment parentFragment; @@ -105,7 +109,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat private boolean ignoreTextChange; private MessageObject replyingMessageObject; private TLRPC.WebPage messageWebPage; - private boolean messageWebPageSearch; + private boolean messageWebPageSearch = true; private ChatActivityEnterViewDelegate delegate; private float topViewAnimation; @@ -114,7 +118,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat private boolean allowShowTopView; private AnimatorSetProxy currentTopViewAnimation; - public ChatActivityEnterView(Activity context, SizeNotifierRelativeLayout parent, BaseFragment fragment, boolean isChat) { + public ChatActivityEnterView(Activity context, View parent, BaseFragment fragment, boolean isChat) { super(context); setBackgroundResource(R.drawable.compose_panel); setFocusable(true); @@ -131,19 +135,48 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat NotificationCenter.getInstance().addObserver(this, NotificationCenter.audioRouteChanged); parentActivity = context; parentFragment = fragment; - sizeNotifierRelativeLayout = parent; - sizeNotifierRelativeLayout.setDelegate(this); + sizeNotifierLayout = parent; + if (sizeNotifierLayout instanceof SizeNotifierRelativeLayout) { + ((SizeNotifierRelativeLayout) sizeNotifierLayout).setDelegate(this); + } else if (sizeNotifierLayout instanceof SizeNotifierFrameLayout) { + ((SizeNotifierFrameLayout) sizeNotifierLayout).setDelegate(this); + } SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); sendByEnter = preferences.getBoolean("send_by_enter", false); + parent.getViewTreeObserver().addOnPreDrawListener(onPreDrawListener = new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + if (keyboardTransitionState == 1) { + if (keyboardVisible || framesDroped >= 60) { + showEmojiPopup(false, false); + keyboardTransitionState = 0; + } else { + openKeyboard(); + } + framesDroped++; + return false; + } else if (keyboardTransitionState == 2) { + if (!keyboardVisible || framesDroped >= 60) { + int currentHeight = AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y ? keyboardHeightLand : keyboardHeight; + sizeNotifierLayout.setPadding(0, 0, 0, currentHeight); + keyboardTransitionState = 0; + } + framesDroped++; + return false; + } + return true; + } + }); + textFieldContainer = new LinearLayout(context); textFieldContainer.setBackgroundColor(0xffffffff); textFieldContainer.setOrientation(LinearLayout.HORIZONTAL); addView(textFieldContainer); LayoutParams layoutParams2 = (LayoutParams) textFieldContainer.getLayoutParams(); layoutParams2.gravity = Gravity.LEFT | Gravity.TOP; - layoutParams2.width = LayoutParams.MATCH_PARENT; - layoutParams2.height = LayoutParams.WRAP_CONTENT; + layoutParams2.width = LayoutHelper.MATCH_PARENT; + layoutParams2.height = LayoutHelper.WRAP_CONTENT; layoutParams2.topMargin = AndroidUtilities.dp(2); textFieldContainer.setLayoutParams(layoutParams2); @@ -151,7 +184,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat textFieldContainer.addView(frameLayout); LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) frameLayout.getLayoutParams(); layoutParams.width = 0; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.weight = 1; frameLayout.setLayoutParams(layoutParams); @@ -168,7 +201,16 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat emojiButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - showEmojiPopup(emojiPopup == null || !emojiPopup.isShowing()); + if (showKeyboardOnEmojiButton) { + setKeyboardTransitionState(1); + int selection = messageEditText.getSelectionStart(); + MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0); + messageEditText.onTouchEvent(event); + event.recycle(); + messageEditText.setSelection(selection); + } else { + showEmojiPopup(emojiPopup == null || !emojiPopup.isShowing(), true); + } } }); @@ -187,8 +229,8 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat messageEditText.setHintTextColor(0xffb2b2b2); frameLayout.addView(messageEditText); layoutParams1 = (FrameLayout.LayoutParams) messageEditText.getLayoutParams(); - layoutParams1.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; layoutParams1.gravity = Gravity.BOTTOM; layoutParams1.leftMargin = AndroidUtilities.dp(52); layoutParams1.rightMargin = AndroidUtilities.dp(isChat ? 50 : 2); @@ -198,7 +240,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat public boolean onKey(View view, int i, KeyEvent keyEvent) { if (i == 4 && !keyboardVisible && emojiPopup != null && emojiPopup.isShowing()) { if (keyEvent.getAction() == 1) { - showEmojiPopup(false); + showEmojiPopup(false, true); } return true; } else if (i == KeyEvent.KEYCODE_ENTER && sendByEnter && keyEvent.getAction() == KeyEvent.ACTION_DOWN) { @@ -212,7 +254,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat @Override public void onClick(View view) { if (emojiPopup != null && emojiPopup.isShowing()) { - showEmojiPopup(false); + setKeyboardTransitionState(1); } } }); @@ -243,10 +285,10 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat checkSendButton(true); if (delegate != null) { - if (before > count || count > 1) { + if (count > 2 || charSequence == null || charSequence.length() == 0) { messageWebPageSearch = true; } - delegate.onTextChanged(charSequence, before > count || count > 1); + delegate.onTextChanged(charSequence, before > count + 1 || (count - before) > 2); } if (message.length() != 0 && lastTypingTimeSend < System.currentTimeMillis() - 5000 && !ignoreTextChange) { @@ -301,7 +343,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat recordPanel.setBackgroundColor(0xffffffff); frameLayout.addView(recordPanel); layoutParams1 = (FrameLayout.LayoutParams) recordPanel.getLayoutParams(); - layoutParams1.width = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; layoutParams1.height = AndroidUtilities.dp(48); layoutParams1.gravity = Gravity.BOTTOM; recordPanel.setLayoutParams(layoutParams1); @@ -310,8 +352,8 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat slideText.setOrientation(LinearLayout.HORIZONTAL); recordPanel.addView(slideText); layoutParams1 = (FrameLayout.LayoutParams) slideText.getLayoutParams(); - layoutParams1.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams1.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams1.width = LayoutHelper.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; layoutParams1.gravity = Gravity.CENTER; layoutParams1.leftMargin = AndroidUtilities.dp(30); slideText.setLayoutParams(layoutParams1); @@ -320,8 +362,8 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat imageView.setImageResource(R.drawable.slidearrow); slideText.addView(imageView); layoutParams = (LinearLayout.LayoutParams) imageView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER_VERTICAL; layoutParams.topMargin = AndroidUtilities.dp(1); imageView.setLayoutParams(layoutParams); @@ -332,8 +374,8 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12); slideText.addView(textView); layoutParams = (LinearLayout.LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER_VERTICAL; layoutParams.leftMargin = AndroidUtilities.dp(6); textView.setLayoutParams(layoutParams); @@ -344,8 +386,8 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat linearLayout.setBackgroundColor(0xffffffff); recordPanel.addView(linearLayout); layoutParams1 = (FrameLayout.LayoutParams) linearLayout.getLayoutParams(); - layoutParams1.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams1.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams1.width = LayoutHelper.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; layoutParams1.gravity = Gravity.CENTER_VERTICAL; linearLayout.setLayoutParams(layoutParams1); @@ -353,8 +395,8 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat imageView.setImageResource(R.drawable.rec); linearLayout.addView(imageView); layoutParams = (LinearLayout.LayoutParams) imageView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER_VERTICAL; layoutParams.topMargin = AndroidUtilities.dp(1); imageView.setLayoutParams(layoutParams); @@ -365,8 +407,8 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat recordTimeText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); linearLayout.addView(recordTimeText); layoutParams = (LinearLayout.LayoutParams) recordTimeText.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER_VERTICAL; layoutParams.leftMargin = AndroidUtilities.dp(6); recordTimeText.setLayoutParams(layoutParams); @@ -395,8 +437,8 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat public boolean onTouch(View view, MotionEvent motionEvent) { if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { if (parentFragment != null) { - String action = null; - TLRPC.Chat currentChat = null; + String action; + TLRPC.Chat currentChat; if ((int) dialog_id < 0) { currentChat = MessagesController.getInstance().getChat(-(int) dialog_id); if (currentChat != null && currentChat.participants_count > MessagesController.getInstance().groupBigSize) { @@ -489,6 +531,26 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat checkSendButton(false); } + private void setKeyboardTransitionState(int state) { + if (AndroidUtilities.usingHardwareInput) { + if (state == 1) { + showEmojiPopup(false, false); + keyboardTransitionState = 0; + + } else if (state == 2) { + int currentHeight = AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y ? keyboardHeightLand : keyboardHeight; + sizeNotifierLayout.setPadding(0, 0, 0, currentHeight); + keyboardTransitionState = 0; + } + } else { + framesDroped = 0; + keyboardTransitionState = state; + if (state == 1) { + sizeNotifierLayout.setPadding(0, 0, 0, 0); + } + } + } + public void addTopView(View view, int height) { if (view == null) { return; @@ -498,7 +560,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat topView.setVisibility(GONE); needShowTopView = false; LayoutParams layoutParams = (LayoutParams) topView.getLayoutParams(); - layoutParams.width = RelativeLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = height; layoutParams.topMargin = AndroidUtilities.dp(2); layoutParams.gravity = Gravity.TOP | Gravity.LEFT; @@ -521,6 +583,10 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat checkSendButton(animated); } + public void setAllowStickers(boolean value) { + allowStickers = value; + } + public void showTopView(boolean animated) { if (topView == null || topViewShowed) { return; @@ -529,9 +595,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat topViewShowed = true; if (allowShowTopView) { topView.setVisibility(VISIBLE); - float resumeValue = 0.0f; if (currentTopViewAnimation != null) { - resumeValue = topViewAnimation; currentTopViewAnimation.cancel(); currentTopViewAnimation = null; } @@ -644,6 +708,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat NotificationCenter.getInstance().removeObserver(this, NotificationCenter.emojiDidLoaded); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.hideEmojiKeyboard); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.audioRouteChanged); + sizeNotifierLayout.getViewTreeObserver().removeOnPreDrawListener(onPreDrawListener); if (mWakeLock != null) { try { mWakeLock.release(); @@ -652,8 +717,12 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat FileLog.e("tmessages", e); } } - if (sizeNotifierRelativeLayout != null) { - sizeNotifierRelativeLayout.setDelegate(null); + if (sizeNotifierLayout != null) { + if (sizeNotifierLayout instanceof SizeNotifierRelativeLayout) { + ((SizeNotifierRelativeLayout) sizeNotifierLayout).setDelegate(null); + } else if (sizeNotifierLayout instanceof SizeNotifierFrameLayout) { + ((SizeNotifierFrameLayout) sizeNotifierLayout).setDelegate(null); + } } } @@ -670,10 +739,14 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat messageWebPageSearch = searchWebPages; } + public boolean isMessageWebPageSearchEnabled() { + return messageWebPageSearch; + } + private void sendMessage() { if (parentFragment != null) { - String action = null; - TLRPC.Chat currentChat = null; + String action; + TLRPC.Chat currentChat; if ((int) dialog_id < 0) { currentChat = MessagesController.getInstance().getChat(-(int) dialog_id); if (currentChat != null && currentChat.participants_count > MessagesController.getInstance().groupBigSize) { @@ -978,16 +1051,20 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat } } - private void showEmojiPopup(boolean show) { + private void showEmojiPopup(boolean show, boolean post) { if (show) { if (emojiPopup == null) { if (parentActivity == null) { return; } - emojiView = new EmojiView(parentActivity); + emojiView = new EmojiView(allowStickers, parentActivity); emojiView.setListener(new EmojiView.Listener() { - public void onBackspace() { - messageEditText.dispatchKeyEvent(new KeyEvent(0, 67)); + public boolean onBackspace() { + if (messageEditText.length() == 0) { + return false; + } + messageEditText.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)); + return true; } public void onEmojiSelected(String symbol) { @@ -995,8 +1072,8 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat if (i < 0) { i = 0; } - try { - CharSequence localCharSequence = Emoji.replaceEmoji(symbol, messageEditText.getPaint().getFontMetricsInt(), AndroidUtilities.dp(20)); + try {//TODO check + CharSequence localCharSequence = Emoji.replaceEmoji(symbol/* + "\uFE0F"*/, messageEditText.getPaint().getFontMetricsInt(), AndroidUtilities.dp(20)); messageEditText.setText(messageEditText.getText().insert(i, localCharSequence)); int j = i + localCharSequence.length(); messageEditText.setSelection(j, j); @@ -1004,24 +1081,16 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat FileLog.e("tmessages", e); } } + + public void onStickerSelected(TLRPC.Document sticker) { + SendMessagesHelper.getInstance().sendSticker(sticker, dialog_id, replyingMessageObject); + if (delegate != null) { + delegate.onMessageSend(null); + } + } }); emojiPopup = new PopupWindow(emojiView); - - if (Build.VERSION.SDK_INT >= 21) { - /*emojiPopup.setAnimationStyle(0); - emojiPopup.setClippingEnabled(true); - emojiPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); - emojiPopup.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED);*/ - try { - Field field = PopupWindow.class.getDeclaredField("mWindowLayoutType"); - field.setAccessible(true); - field.set(emojiPopup, WindowManager.LayoutParams.TYPE_SYSTEM_ERROR); - } catch (Exception e) { - //ignored - } - } } - int currentHeight; if (keyboardHeight <= 0) { keyboardHeight = ApplicationLoader.applicationContext.getSharedPreferences("emoji", 0).getInt("kbd_height", AndroidUtilities.dp(200)); @@ -1029,59 +1098,33 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat if (keyboardHeightLand <= 0) { keyboardHeightLand = ApplicationLoader.applicationContext.getSharedPreferences("emoji", 0).getInt("kbd_height_land3", AndroidUtilities.dp(200)); } - if (AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y) { - currentHeight = keyboardHeightLand; - } else { - currentHeight = keyboardHeight; - } + int currentHeight = AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y ? keyboardHeightLand : keyboardHeight; FileLog.e("tmessages", "show emoji with height = " + currentHeight); emojiPopup.setHeight(View.MeasureSpec.makeMeasureSpec(currentHeight, View.MeasureSpec.EXACTLY)); - if (sizeNotifierRelativeLayout != null) { + if (sizeNotifierLayout != null) { emojiPopup.setWidth(View.MeasureSpec.makeMeasureSpec(AndroidUtilities.displaySize.x, View.MeasureSpec.EXACTLY)); } emojiPopup.showAtLocation(parentActivity.getWindow().getDecorView(), Gravity.BOTTOM | Gravity.LEFT, 0, 0); - /*if (Build.VERSION.SDK_INT < 21) { - try { - - } catch (Exception e) { - FileLog.e("tmessages", e); - return; - } - }*/ - if (!keyboardVisible) { - /*if (Build.VERSION.SDK_INT >= 21) { - try { - emojiPopup.showAsDropDown(this, 0, 0); - } catch (Exception e) { - FileLog.e("tmessages", e); - return; - } - }*/ - if (sizeNotifierRelativeLayout != null) { - sizeNotifierRelativeLayout.setPadding(0, 0, 0, currentHeight); + if (sizeNotifierLayout != null) { + sizeNotifierLayout.setPadding(0, 0, 0, currentHeight); emojiButton.setImageResource(R.drawable.ic_msg_panel_hide); - onWindowSizeChanged(sizeNotifierRelativeLayout.getHeight() - sizeNotifierRelativeLayout.getPaddingBottom()); + showKeyboardOnEmojiButton = false; + onWindowSizeChanged(sizeNotifierLayout.getHeight() - sizeNotifierLayout.getPaddingBottom()); } return; } else { - /*if (Build.VERSION.SDK_INT >= 21) { - try { - emojiPopup.showAsDropDown(this, 0, -currentHeight - getHeight()); - emojiPopup.update(this, 0, -currentHeight - getHeight(), -1, -1); - AndroidUtilities.hideKeyboard(messageEditText); - } catch (Exception e) { - FileLog.e("tmessages", e); - return; - } - }*/ + setKeyboardTransitionState(2); + AndroidUtilities.hideKeyboard(messageEditText); } emojiButton.setImageResource(R.drawable.ic_msg_panel_kb); + showKeyboardOnEmojiButton = true; return; } if (emojiButton != null) { + showKeyboardOnEmojiButton = false; emojiButton.setImageResource(R.drawable.ic_msg_panel_smiles); } if (emojiPopup != null) { @@ -1091,21 +1134,28 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat //don't promt } } - if (sizeNotifierRelativeLayout != null) { - sizeNotifierRelativeLayout.post(new Runnable() { - public void run() { - if (sizeNotifierRelativeLayout != null) { - sizeNotifierRelativeLayout.setPadding(0, 0, 0, 0); - onWindowSizeChanged(sizeNotifierRelativeLayout.getHeight() - sizeNotifierRelativeLayout.getPaddingBottom()); - } + if (keyboardTransitionState == 0) { + if (sizeNotifierLayout != null) { + if (post) { + sizeNotifierLayout.post(new Runnable() { + public void run() { + if (sizeNotifierLayout != null) { + sizeNotifierLayout.setPadding(0, 0, 0, 0); + onWindowSizeChanged(sizeNotifierLayout.getHeight()); + } + } + }); + } else { + sizeNotifierLayout.setPadding(0, 0, 0, 0); + onWindowSizeChanged(sizeNotifierLayout.getHeight()); } - }); + } } } public void hideEmojiPopup() { if (emojiPopup != null && emojiPopup.isShowing()) { - showEmojiPopup(false); + showEmojiPopup(false, true); } } @@ -1201,7 +1251,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) view.getLayoutParams(); layoutParams.gravity = Gravity.CENTER; layoutParams.width = AndroidUtilities.dp(48); - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; view.setLayoutParams(layoutParams); } @@ -1218,32 +1268,20 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat } if (emojiPopup != null && emojiPopup.isShowing()) { - int newHeight = 0; - if (isWidthGreater) { - newHeight = keyboardHeightLand; - } else { - newHeight = keyboardHeight; - } + int newHeight = isWidthGreater ? keyboardHeightLand : keyboardHeight; final WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) emojiPopup.getContentView().getLayoutParams(); FileLog.e("tmessages", "update emoji height to = " + newHeight); if (layoutParams.width != AndroidUtilities.displaySize.x || layoutParams.height != newHeight) { - /*if (Build.VERSION.SDK_INT >= 21) { - if (!keyboardVisible) { - emojiPopup.update(this, 0, 0, -1, -1); - } else { - emojiPopup.update(this, 0, -newHeight - getHeight(), -1, -1); - } - }*/ layoutParams.width = AndroidUtilities.displaySize.x; layoutParams.height = newHeight; WindowManager wm = (WindowManager) ApplicationLoader.applicationContext.getSystemService(Activity.WINDOW_SERVICE); if (wm != null) { wm.updateViewLayout(emojiPopup.getContentView(), layoutParams); if (!keyboardVisible) { - if (sizeNotifierRelativeLayout != null) { - sizeNotifierRelativeLayout.setPadding(0, 0, 0, layoutParams.height); - sizeNotifierRelativeLayout.requestLayout(); - onWindowSizeChanged(sizeNotifierRelativeLayout.getHeight() - sizeNotifierRelativeLayout.getPaddingBottom()); + if (sizeNotifierLayout != null) { + sizeNotifierLayout.setPadding(0, 0, 0, layoutParams.height); + sizeNotifierLayout.requestLayout(); + onWindowSizeChanged(sizeNotifierLayout.getHeight() - sizeNotifierLayout.getPaddingBottom()); } } } @@ -1252,12 +1290,22 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat boolean oldValue = keyboardVisible; keyboardVisible = height > 0; - if (keyboardVisible && sizeNotifierRelativeLayout.getPaddingBottom() > 0) { - showEmojiPopup(false); - } else if (!keyboardVisible && keyboardVisible != oldValue && emojiPopup != null && emojiPopup.isShowing()) { - showEmojiPopup(false); + if (keyboardVisible && (sizeNotifierLayout.getPaddingBottom() > 0 || keyboardTransitionState == 1)) { + setKeyboardTransitionState(1); + } else if (keyboardTransitionState != 2 && !keyboardVisible && keyboardVisible != oldValue && emojiPopup != null && emojiPopup.isShowing()) { + showEmojiPopup(false, true); + } + if (keyboardTransitionState == 0) { + onWindowSizeChanged(sizeNotifierLayout.getHeight() - sizeNotifierLayout.getPaddingBottom()); + } + } + + public int getEmojiHeight() { + if (AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y) { + return keyboardHeightLand; + } else { + return keyboardHeight; } - onWindowSizeChanged(sizeNotifierRelativeLayout.getHeight() - sizeNotifierRelativeLayout.getPaddingBottom()); } @Override @@ -1270,6 +1318,9 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat Long time = (Long) args[0] / 1000; String str = String.format("%02d:%02d", time / 60, time % 60); if (lastTimeString == null || !lastTimeString.equals(str)) { + if (time % 5 == 0) { + MessagesController.getInstance().sendTyping(dialog_id, 1, 0); + } if (recordTimeText != null) { recordTimeText.setText(str); } @@ -1280,6 +1331,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat } } else if (id == NotificationCenter.recordStartError || id == NotificationCenter.recordStopped) { if (recordingAudio) { + MessagesController.getInstance().sendTyping(dialog_id, 2, 0); recordingAudio = false; updateAudioRecordIntefrace(); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/CheckBox.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/CheckBox.java index a34b7a989..95707a7f4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/CheckBox.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/CheckBox.java @@ -18,7 +18,7 @@ import android.graphics.drawable.Drawable; import android.view.View; import org.telegram.android.AndroidUtilities; -import org.telegram.ui.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; public class CheckBox extends View { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ClippingImageView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ClippingImageView.java index 3c17e47ee..71ec5be0f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ClippingImageView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ClippingImageView.java @@ -19,7 +19,7 @@ import android.graphics.Shader; import android.view.View; import org.telegram.messenger.FileLog; -import org.telegram.ui.AnimationCompat.ViewProxy; +import org.telegram.android.AnimationCompat.ViewProxy; public class ClippingImageView extends View { @@ -32,7 +32,6 @@ public class ClippingImageView extends View { private Paint paint; private Bitmap bmp; private Matrix matrix; - private onDrawListener drawListener; private boolean needRadius; private int radius; @@ -42,9 +41,8 @@ public class ClippingImageView extends View { private RectF bitmapRect; private Matrix shaderMatrix; - public interface onDrawListener { - void onDraw(); - } + private float animationProgress; + private float animationValues[][]; public ClippingImageView(Context context) { super(context); @@ -55,6 +53,29 @@ public class ClippingImageView extends View { bitmapRect = new RectF(); } + public void setAnimationValues(float[][] values) { + animationValues = values; + } + + public float getAnimationProgress() { + return animationProgress; + } + + public void setAnimationProgress(float progress) { + animationProgress = progress; + + ViewProxy.setScaleX(this, animationValues[0][0] + (animationValues[1][0] - animationValues[0][0]) * animationProgress); + ViewProxy.setScaleY(this, animationValues[0][1] + (animationValues[1][1] - animationValues[0][1]) * animationProgress); + ViewProxy.setTranslationX(this, animationValues[0][2] + (animationValues[1][2] - animationValues[0][2]) * animationProgress); + ViewProxy.setTranslationY(this, animationValues[0][3] + (animationValues[1][3] - animationValues[0][3]) * animationProgress); + setClipHorizontal((int) (animationValues[0][4] + (animationValues[1][4] - animationValues[0][4]) * animationProgress)); + setClipTop((int) (animationValues[0][5] + (animationValues[1][5] - animationValues[0][5]) * animationProgress)); + setClipBottom((int) (animationValues[0][6] + (animationValues[1][6] - animationValues[0][6]) * animationProgress)); + setRadius((int) (animationValues[0][7] + (animationValues[1][7] - animationValues[0][7]) * animationProgress)); + + invalidate(); + } + public int getClipBottom() { return clipBottom; } @@ -85,9 +106,6 @@ public class ClippingImageView extends View { } if (bmp != null) { float scaleY = ViewProxy.getScaleY(this); - if (drawListener != null && scaleY != 1) { - drawListener.onDraw(); - } canvas.save(); if (needRadius) { @@ -174,10 +192,6 @@ public class ClippingImageView extends View { invalidate(); } - public void setOnDrawListener(onDrawListener listener) { - drawListener = listener; - } - public void setNeedRadius(boolean value) { needRadius = value; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java index 2f2706d4d..260b398c1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java @@ -8,12 +8,16 @@ package org.telegram.ui.Components; +import android.app.Activity; import android.content.Context; +import android.content.SharedPreferences; import android.database.DataSetObserver; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.text.TextUtils; -import android.util.AttributeSet; +import android.view.Gravity; +import android.view.HapticFeedbackConstants; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; @@ -26,61 +30,207 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.Emoji; import org.telegram.android.LocaleController; +import org.telegram.android.NotificationCenter; +import org.telegram.android.query.StickersQuery; +import org.telegram.messenger.FileLog; import org.telegram.messenger.R; +import org.telegram.messenger.TLRPC; +import org.telegram.ui.Cells.StickerEmojiCell; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; -public class EmojiView extends LinearLayout { +public class EmojiView extends LinearLayout implements NotificationCenter.NotificationCenterDelegate { + + public interface Listener { + boolean onBackspace(); + void onEmojiSelected(String emoji); + void onStickerSelected(TLRPC.Document sticker); + } private ArrayList adapters = new ArrayList<>(); + private StickersGridAdapter stickersGridAdapter; + private HashMap stickersUseHistory = new HashMap<>(); + private ArrayList stickers; + private int[] icons = { R.drawable.ic_emoji_recent, R.drawable.ic_emoji_smile, R.drawable.ic_emoji_flower, R.drawable.ic_emoji_bell, R.drawable.ic_emoji_car, - R.drawable.ic_emoji_symbol }; + R.drawable.ic_emoji_symbol, + R.drawable.ic_emoji_sticker}; + private Listener listener; private ViewPager pager; private FrameLayout recentsWrap; + private FrameLayout emojiWrap; private ArrayList views = new ArrayList<>(); + private ImageView backspaceButton; - public EmojiView(Context paramContext) { - super(paramContext); - init(); + private boolean backspacePressed; + private boolean backspaceOnce; + private boolean showStickers; + + public EmojiView(boolean needStickers, Context context) { + super(context); + + showStickers = needStickers; + + setOrientation(LinearLayout.VERTICAL); + for (int i = 0; i < Emoji.data.length; i++) { + GridView gridView = new GridView(context); + if (AndroidUtilities.isTablet()) { + gridView.setColumnWidth(AndroidUtilities.dp(60)); + } else { + gridView.setColumnWidth(AndroidUtilities.dp(45)); + } + gridView.setNumColumns(-1); + views.add(gridView); + + EmojiGridAdapter emojiGridAdapter = new EmojiGridAdapter(Emoji.data[i]); + gridView.setAdapter(emojiGridAdapter); + AndroidUtilities.setListViewEdgeEffectColor(gridView, 0xfff5f6f7); + adapters.add(emojiGridAdapter); + } + + if (showStickers) { + StickersQuery.checkStickers(); + stickers = StickersQuery.getStickers(); + GridView gridView = new GridView(context); + gridView.setColumnWidth(AndroidUtilities.dp(72)); + gridView.setNumColumns(-1); + gridView.setPadding(0, AndroidUtilities.dp(4), 0, 0); + gridView.setClipToPadding(false); + views.add(gridView); + stickersGridAdapter = new StickersGridAdapter(context); + gridView.setAdapter(stickersGridAdapter); + AndroidUtilities.setListViewEdgeEffectColor(gridView, 0xfff5f6f7); + } + + setBackgroundColor(0xfff5f6f7); + + pager = new ViewPager(context); + pager.setAdapter(new EmojiPagesAdapter()); + + LinearLayout linearLayout = new LinearLayout(context); + linearLayout.setOrientation(LinearLayout.HORIZONTAL); + linearLayout.setBackgroundColor(0xfff5f6f7); + addView(linearLayout, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, AndroidUtilities.dp(48))); + + PagerSlidingTabStrip tabs = new PagerSlidingTabStrip(context); + tabs.setViewPager(pager); + tabs.setShouldExpand(true); + tabs.setIndicatorHeight(AndroidUtilities.dp(2)); + tabs.setUnderlineHeight(AndroidUtilities.dp(1)); + tabs.setIndicatorColor(0xff2b96e2); + tabs.setUnderlineColor(0xffe2e5e7); + linearLayout.addView(tabs, new LinearLayout.LayoutParams(0, AndroidUtilities.dp(48), 1.0f)); + + FrameLayout frameLayout = new FrameLayout(context); + linearLayout.addView(frameLayout, new LinearLayout.LayoutParams(AndroidUtilities.dp(52), AndroidUtilities.dp(48))); + + backspaceButton = new ImageView(context) { + @Override + public boolean onTouchEvent(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + backspacePressed = true; + backspaceOnce = false; + postBackspaceRunnable(350); + } else if (event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_UP) { + backspacePressed = false; + if (!backspaceOnce) { + if (EmojiView.this.listener != null && EmojiView.this.listener.onBackspace()) { + backspaceButton.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + } + } + } + super.onTouchEvent(event); + return true; + } + }; + backspaceButton.setImageResource(R.drawable.ic_smiles_backspace); + backspaceButton.setBackgroundResource(R.drawable.ic_emoji_backspace); + backspaceButton.setScaleType(ImageView.ScaleType.CENTER); + frameLayout.addView(backspaceButton, new FrameLayout.LayoutParams(AndroidUtilities.dp(52), AndroidUtilities.dp(48))); + + View view = new View(context); + view.setBackgroundColor(0xffe2e5e7); + frameLayout.addView(view, new FrameLayout.LayoutParams(AndroidUtilities.dp(52), AndroidUtilities.dp(1), Gravity.LEFT | Gravity.BOTTOM)); + + recentsWrap = new FrameLayout(context); + recentsWrap.addView(views.get(0)); + + TextView textView = new TextView(context); + textView.setText(LocaleController.getString("NoRecent", R.string.NoRecent)); + textView.setTextSize(18); + textView.setTextColor(0xff888888); + textView.setGravity(Gravity.CENTER); + recentsWrap.addView(textView); + views.get(0).setEmptyView(textView); + + if (views.size() > 6) { + emojiWrap = new FrameLayout(context); + emojiWrap.addView(views.get(6)); + + textView = new TextView(context); + textView.setText(LocaleController.getString("NoStickers", R.string.NoStickers)); + textView.setTextSize(18); + textView.setTextColor(0xff888888); + textView.setGravity(Gravity.CENTER); + emojiWrap.addView(textView); + views.get(6).setEmptyView(textView); + } + + addView(pager); + + loadRecents(); + + if (Emoji.data[0] == null || Emoji.data[0].length == 0) { + pager.setCurrentItem(1); + } } - public EmojiView(Context paramContext, AttributeSet paramAttributeSet) { - super(paramContext, paramAttributeSet); - init(); + private void postBackspaceRunnable(final int time) { + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + if (!backspacePressed) { + return; + } + if (EmojiView.this.listener != null && EmojiView.this.listener.onBackspace()) { + backspaceButton.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + } + backspaceOnce = true; + postBackspaceRunnable(Math.max(50, time - 100)); + } + }, time); } - public EmojiView(Context paramContext, AttributeSet paramAttributeSet, int paramInt) { - super(paramContext, paramAttributeSet, paramInt); - init(); - } - - private void addToRecent(long paramLong) { - if (this.pager.getCurrentItem() == 0) { + private void addToRecent(long code) { + if (pager.getCurrentItem() == 0) { return; } - ArrayList localArrayList = new ArrayList<>(); + ArrayList recent = new ArrayList<>(); long[] currentRecent = Emoji.data[0]; boolean was = false; for (long aCurrentRecent : currentRecent) { - if (paramLong == aCurrentRecent) { - localArrayList.add(0, paramLong); + if (code == aCurrentRecent) { + recent.add(0, code); was = true; } else { - localArrayList.add(aCurrentRecent); + recent.add(aCurrentRecent); } } if (!was) { - localArrayList.add(0, paramLong); + recent.add(0, code); } - Emoji.data[0] = new long[Math.min(localArrayList.size(), 50)]; + Emoji.data[0] = new long[Math.min(recent.size(), 50)]; for (int q = 0; q < Emoji.data[0].length; q++) { - Emoji.data[0][q] = localArrayList.get(q); + Emoji.data[0][q] = recent.get(q); } adapters.get(0).data = Emoji.data[0]; adapters.get(0).notifyDataSetChanged(); @@ -93,109 +243,119 @@ public class EmojiView extends LinearLayout { if (i >= 4) { return str; } - int j = (int)(0xFFFF & paramLong >> 16 * (3 - i)); + int j = (int) (0xFFFF & paramLong >> 16 * (3 - i)); if (j != 0) { - str = str + (char)j; + str = str + (char) j; } } } - private void init() { - setOrientation(LinearLayout.VERTICAL); - for (int i = 0; i < Emoji.data.length; i++) { - GridView gridView = new GridView(getContext()); - if (AndroidUtilities.isTablet()) { - gridView.setColumnWidth(AndroidUtilities.dp(60)); - } else { - gridView.setColumnWidth(AndroidUtilities.dp(45)); - } - gridView.setNumColumns(-1); - views.add(gridView); - - EmojiGridAdapter localEmojiGridAdapter = new EmojiGridAdapter(Emoji.data[i]); - gridView.setAdapter(localEmojiGridAdapter); - AndroidUtilities.setListViewEdgeEffectColor(gridView, 0xff999999); - adapters.add(localEmojiGridAdapter); - } - - setBackgroundColor(0xff222222); - pager = new ViewPager(getContext()); - pager.setAdapter(new EmojiPagesAdapter()); - PagerSlidingTabStrip tabs = new PagerSlidingTabStrip(getContext()); - tabs.setViewPager(pager); - tabs.setShouldExpand(true); - tabs.setIndicatorColor(0xff33b5e5); - tabs.setIndicatorHeight(AndroidUtilities.dp(2.0f)); - tabs.setUnderlineHeight(AndroidUtilities.dp(2.0f)); - tabs.setUnderlineColor(0x66000000); - tabs.setTabBackground(0); - LinearLayout localLinearLayout = new LinearLayout(getContext()); - localLinearLayout.setOrientation(LinearLayout.HORIZONTAL); - localLinearLayout.addView(tabs, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 1.0f)); - ImageView localImageView = new ImageView(getContext()); - localImageView.setImageResource(R.drawable.ic_emoji_backspace); - localImageView.setScaleType(ImageView.ScaleType.CENTER); - localImageView.setBackgroundResource(R.drawable.bg_emoji_bs); - localImageView.setOnClickListener(new View.OnClickListener() { - public void onClick(View view) { - if (EmojiView.this.listener != null) { - EmojiView.this.listener.onBackspace(); - } - } - }); - localLinearLayout.addView(localImageView, new LinearLayout.LayoutParams(AndroidUtilities.dp(61), LayoutParams.MATCH_PARENT)); - recentsWrap = new FrameLayout(getContext()); - recentsWrap.addView(views.get(0)); - TextView localTextView = new TextView(getContext()); - localTextView.setText(LocaleController.getString("NoRecent", R.string.NoRecent)); - localTextView.setTextSize(18.0f); - localTextView.setTextColor(-7829368); - localTextView.setGravity(17); - recentsWrap.addView(localTextView); - views.get(0).setEmptyView(localTextView); - addView(localLinearLayout, new LinearLayout.LayoutParams(-1, AndroidUtilities.dp(48.0f))); - addView(pager); - loadRecents(); - if (Emoji.data[0] == null || Emoji.data[0].length == 0) { - pager.setCurrentItem(1); - } - } - private void saveRecents() { - ArrayList localArrayList = new ArrayList<>(); - long[] arrayOfLong = Emoji.data[0]; - int i = arrayOfLong.length; - for (int j = 0; ; j++) { - if (j >= i) { - getContext().getSharedPreferences("emoji", 0).edit().putString("recents", TextUtils.join(",", localArrayList)).commit(); - return; - } - localArrayList.add(arrayOfLong[j]); + ArrayList arrayList = new ArrayList<>(Emoji.data[0].length); + for (int j = 0; j < Emoji.data[0].length; j++) { + arrayList.add(Emoji.data[0][j]); } + getContext().getSharedPreferences("emoji", 0).edit().putString("recents", TextUtils.join(",", arrayList)).commit(); + } + + private void saveRecentStickers() { + SharedPreferences preferences = getContext().getSharedPreferences("emoji", Activity.MODE_PRIVATE); + StringBuilder stringBuilder = new StringBuilder(); + for (HashMap.Entry entry : stickersUseHistory.entrySet()) { + if (stringBuilder.length() != 0) { + stringBuilder.append(","); + } + stringBuilder.append(entry.getKey()); + stringBuilder.append("="); + stringBuilder.append(entry.getValue()); + } + getContext().getSharedPreferences("emoji", 0).edit().putString("stickers", stringBuilder.toString()).commit(); + } + + private void sortStickers() { + HashMap hashMap = new HashMap<>(); + for (TLRPC.Document document : stickers) { + Integer count = stickersUseHistory.get(document.id); + if (count != null) { + hashMap.put(document.id, count); + stickersUseHistory.remove(document.id); + } + } + if (!stickersUseHistory.isEmpty()) { + stickersUseHistory = hashMap; + saveRecents(); + } else { + stickersUseHistory = hashMap; + } + Collections.sort(stickers, new Comparator() { + @Override + public int compare(TLRPC.Document lhs, TLRPC.Document rhs) { + Integer count1 = stickersUseHistory.get(lhs.id); + Integer count2 = stickersUseHistory.get(rhs.id); + if (count1 == null) { + count1 = 0; + } + if (count2 == null) { + count2 = 0; + } + if (count1 > count2) { + return -1; + } else if (count1 < count2) { + return 1; + } + return 0; + } + }); } public void loadRecents() { - String str = getContext().getSharedPreferences("emoji", 0).getString("recents", ""); - String[] arrayOfString = null; - if ((str != null) && (str.length() > 0)) { - arrayOfString = str.split(","); - Emoji.data[0] = new long[arrayOfString.length]; - } - if (arrayOfString != null) { - for (int i = 0; i < arrayOfString.length; i++) { - Emoji.data[0][i] = Long.parseLong(arrayOfString[i]); + SharedPreferences preferences = getContext().getSharedPreferences("emoji", Activity.MODE_PRIVATE); + String str = preferences.getString("recents", ""); + try { + if (str != null && str.length() > 0) { + String[] args = str.split(","); + Emoji.data[0] = new long[args.length]; + for (int i = 0; i < args.length; i++) { + Emoji.data[0][i] = Long.parseLong(args[i]); + } + } else { + Emoji.data[0] = new long[]{0x00000000D83DDE02L, 0x00000000D83DDE18L, 0x0000000000002764L, 0x00000000D83DDE0DL, 0x00000000D83DDE0AL, 0x00000000D83DDE01L, + 0x00000000D83DDC4DL, 0x000000000000263AL, 0x00000000D83DDE14L, 0x00000000D83DDE04L, 0x00000000D83DDE2DL, 0x00000000D83DDC8BL, + 0x00000000D83DDE12L, 0x00000000D83DDE33L, 0x00000000D83DDE1CL, 0x00000000D83DDE48L, 0x00000000D83DDE09L, 0x00000000D83DDE03L, + 0x00000000D83DDE22L, 0x00000000D83DDE1DL, 0x00000000D83DDE31L, 0x00000000D83DDE21L, 0x00000000D83DDE0FL, 0x00000000D83DDE1EL, + 0x00000000D83DDE05L, 0x00000000D83DDE1AL, 0x00000000D83DDE4AL, 0x00000000D83DDE0CL, 0x00000000D83DDE00L, 0x00000000D83DDE0BL, + 0x00000000D83DDE06L, 0x00000000D83DDC4CL, 0x00000000D83DDE10L, 0x00000000D83DDE15L}; } adapters.get(0).data = Emoji.data[0]; adapters.get(0).notifyDataSetChanged(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + + if (showStickers) { + try { + stickersUseHistory.clear(); + str = preferences.getString("stickers", ""); + if (str != null && str.length() > 0) { + String[] args = str.split(","); + for (String arg : args) { + String[] args2 = arg.split("="); + stickersUseHistory.put(Long.parseLong(args2[0]), Integer.parseInt(args2[1])); + } + } + sortStickers(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } } } - public void onMeasure(int paramInt1, int paramInt2) { - super.onMeasure(View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.getSize(paramInt1), MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.getSize(paramInt2), MeasureSpec.EXACTLY)); + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY)); } - public void setListener(Listener paramListener) { - this.listener = paramListener; + public void setListener(Listener value) { + listener = value; } public void invalidateViews() { @@ -206,6 +366,87 @@ public class EmojiView extends LinearLayout { } } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (stickersGridAdapter != null) { + NotificationCenter.getInstance().addObserver(this, NotificationCenter.stickersDidLoaded); + stickers = StickersQuery.getStickers(); + sortStickers(); + stickersGridAdapter.notifyDataSetChanged(); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (stickersGridAdapter != null) { + NotificationCenter.getInstance().removeObserver(this, NotificationCenter.stickersDidLoaded); + } + } + + @Override + public void didReceivedNotification(int id, Object... args) { + if (id == NotificationCenter.stickersDidLoaded) { + stickersGridAdapter.notifyDataSetChanged(); + } + } + + private class StickersGridAdapter extends BaseAdapter { + + Context context; + + public StickersGridAdapter(Context context) { + this.context = context; + } + + public int getCount() { + return stickers.size(); + } + + public Object getItem(int i) { + return stickers.get(i); + } + + public long getItemId(int i) { + return stickers.get(i).id; + } + + public View getView(int i, View view, ViewGroup viewGroup) { + if (view == null) { + view = new StickerEmojiCell(context) { + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(82), MeasureSpec.EXACTLY)); + } + }; + view.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (listener != null) { + TLRPC.Document document = ((StickerEmojiCell) v).getSticker(); + Integer count = stickersUseHistory.get(document.id); + if (count == null) { + count = 0; + } + stickersUseHistory.put(document.id, ++count); + saveRecentStickers(); + listener.onStickerSelected(document); + } + } + }); + } + ((StickerEmojiCell) view).setSticker(stickers.get(i), false); + return view; + } + + @Override + public void unregisterDataSetObserver(DataSetObserver observer) { + if (observer != null) { + super.unregisterDataSetObserver(observer); + } + } + } + private class EmojiGridAdapter extends BaseAdapter { long[] data; @@ -263,6 +504,8 @@ public class EmojiView extends LinearLayout { View localObject; if (paramInt == 0) { localObject = recentsWrap; + } else if (paramInt == 6) { + localObject = emojiWrap; } else { localObject = views.get(paramInt); } @@ -281,6 +524,8 @@ public class EmojiView extends LinearLayout { View localObject; if (paramInt == 0) { localObject = recentsWrap; + } else if (paramInt == 6) { + localObject = emojiWrap; } else { localObject = views.get(paramInt); } @@ -299,9 +544,4 @@ public class EmojiView extends LinearLayout { } } } - - public interface Listener { - void onBackspace(); - void onEmojiSelected(String paramString); - } } \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmptyTextProgressView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmptyTextProgressView.java new file mode 100644 index 000000000..0e38769e6 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmptyTextProgressView.java @@ -0,0 +1,103 @@ +/* + * This is the source code of Telegram for Android v. 2.x.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2015. + */ + +package org.telegram.ui.Components; + +import android.content.Context; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ProgressBar; +import android.widget.TextView; + +import org.telegram.android.LocaleController; +import org.telegram.messenger.R; + +public class EmptyTextProgressView extends FrameLayout { + + private TextView textView; + private ProgressBar progressBar; + private boolean inLayout; + private boolean showAtCenter; + + public EmptyTextProgressView(Context context) { + super(context); + + progressBar = new ProgressBar(context); + progressBar.setVisibility(INVISIBLE); + addView(progressBar, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); + + textView = new TextView(context); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); + textView.setTextColor(0xff808080); + textView.setGravity(Gravity.CENTER); + textView.setVisibility(INVISIBLE); + textView.setText(LocaleController.getString("NoResult", R.string.NoResult)); + addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); + + setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + return true; + } + }); + } + + public void showProgress() { + textView.setVisibility(INVISIBLE); + progressBar.setVisibility(VISIBLE); + } + + public void showTextView() { + textView.setVisibility(VISIBLE); + progressBar.setVisibility(INVISIBLE); + } + + public void setText(String text) { + textView.setText(text); + } + + public void setShowAtCenter(boolean value) { + showAtCenter = value; + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + inLayout = true; + int width = r - l; + int height = b - t; + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + + if (child.getVisibility() == GONE) { + continue; + } + + LayoutParams lp = (LayoutParams) child.getLayoutParams(); + int x = (width - child.getMeasuredWidth()) / 2; + int y; + if (showAtCenter) { + y = (height / 2 - child.getMeasuredHeight()) / 2; + } else { + y = (height - child.getMeasuredHeight()) / 2; + } + child.layout(x, y, x + child.getMeasuredWidth(), y + child.getMeasuredHeight()); + } + inLayout = false; + } + + @Override + public void requestLayout() { + if (!inLayout) { + super.requestLayout(); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ForegroundDetector.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ForegroundDetector.java index a1f4cf2f6..26323f5e9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ForegroundDetector.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ForegroundDetector.java @@ -8,6 +8,7 @@ package org.telegram.ui.Components; +import android.annotation.SuppressLint; import android.app.Activity; import android.app.Application; import android.os.Build; @@ -17,6 +18,7 @@ import org.telegram.messenger.FileLog; import java.util.concurrent.CopyOnWriteArrayList; +@SuppressLint("NewApi") public class ForegroundDetector implements Application.ActivityLifecycleCallbacks { public interface Listener { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/FrameLayoutFixed.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/FrameLayoutFixed.java index 542623e9d..cd169b3f9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/FrameLayoutFixed.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/FrameLayoutFixed.java @@ -87,8 +87,8 @@ public class FrameLayoutFixed extends FrameLayout { child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); childState |= getMeasuredStateFixed(child); if (measureMatchParentChildren) { - if (lp.width == LayoutParams.MATCH_PARENT || - lp.height == LayoutParams.MATCH_PARENT) { + if (lp.width == LayoutHelper.MATCH_PARENT || + lp.height == LayoutHelper.MATCH_PARENT) { mMatchParentChildren.add(child); } } @@ -122,7 +122,7 @@ public class FrameLayoutFixed extends FrameLayout { int childWidthMeasureSpec; int childHeightMeasureSpec; - if (lp.width == LayoutParams.MATCH_PARENT) { + if (lp.width == LayoutHelper.MATCH_PARENT) { childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - lp.leftMargin - lp.rightMargin, @@ -134,7 +134,7 @@ public class FrameLayoutFixed extends FrameLayout { lp.width); } - if (lp.height == LayoutParams.MATCH_PARENT) { + if (lp.height == LayoutHelper.MATCH_PARENT) { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() - getPaddingTop() - getPaddingBottom() - lp.topMargin - lp.bottomMargin, diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/HorizontalListView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/HorizontalListView.java deleted file mode 100644 index 38728e89b..000000000 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/HorizontalListView.java +++ /dev/null @@ -1,389 +0,0 @@ -/* - * This is the source code of Telegram for Android v. 1.3.2. - * 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. - */ -package org.telegram.ui.Components; - -import java.util.HashMap; -import java.util.LinkedList; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.database.DataSetObserver; -import android.graphics.Rect; -import android.util.AttributeSet; -import android.view.GestureDetector; -import android.view.GestureDetector.OnGestureListener; -import android.view.MotionEvent; -import android.view.View; -import android.widget.AdapterView; -import android.widget.ListAdapter; -import android.widget.Scroller; - -public class HorizontalListView extends AdapterView { - - public boolean mAlwaysOverrideTouch = true; - protected ListAdapter mAdapter; - private int mLeftViewIndex = -1; - private int mRightViewIndex = 0; - protected int mCurrentX; - protected int mNextX; - private int mMaxX = Integer.MAX_VALUE; - private int mDisplayOffset = 0; - protected Scroller mScroller; - private GestureDetector mGesture; - private HashMap> mRemovedViewQueue = new HashMap<>(); - private OnItemSelectedListener mOnItemSelected; - private OnItemClickListener mOnItemClicked; - private OnItemLongClickListener mOnItemLongClicked; - private boolean mDataChanged = false; - - public HorizontalListView(Context context, AttributeSet attrs) { - super(context, attrs); - initView(); - } - - private synchronized void initView() { - mLeftViewIndex = -1; - mRightViewIndex = 0; - mDisplayOffset = 0; - mCurrentX = 0; - mNextX = 0; - mMaxX = Integer.MAX_VALUE; - mScroller = new Scroller(getContext()); - mGesture = new GestureDetector(getContext(), mOnGesture); - } - - @Override - public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) { - mOnItemSelected = listener; - } - - @Override - public void setOnItemClickListener(AdapterView.OnItemClickListener listener) { - mOnItemClicked = listener; - } - - @Override - public void setOnItemLongClickListener(AdapterView.OnItemLongClickListener listener) { - mOnItemLongClicked = listener; - } - - private DataSetObserver mDataObserver = new DataSetObserver() { - @Override - public void onChanged() { - synchronized (HorizontalListView.this) { - mDataChanged = true; - } - invalidate(); - requestLayout(); - } - - @Override - public void onInvalidated() { - reset(); - invalidate(); - requestLayout(); - } - }; - - @Override - public ListAdapter getAdapter() { - return mAdapter; - } - - @Override - public View getSelectedView() { - - return null; - } - - @Override - public void setSelection(int position) { - - } - - @Override - public void setAdapter(ListAdapter adapter) { - if (mAdapter != null && mDataObserver != null) { - mAdapter.unregisterDataSetObserver(mDataObserver); - } - mAdapter = adapter; - mAdapter.registerDataSetObserver(mDataObserver); - reset(); - } - - private synchronized void reset() { - initView(); - mRemovedViewQueue.clear(); - removeAllViewsInLayout(); - requestLayout(); - } - - private void addAndMeasureChild(final View child, int viewPos) { - LayoutParams params = child.getLayoutParams(); - if (params == null) { - params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); - } - addViewInLayout(child, viewPos, params, true); - child.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST)); - } - - - @SuppressLint("DrawAllocation") - @Override - protected synchronized void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - - if (mAdapter == null) { - return; - } - - if (mDataChanged) { - int oldCurrentX = mCurrentX; - initView(); - removeAllViewsInLayout(); - mNextX = oldCurrentX; - mDataChanged = false; - } - - if (mScroller.computeScrollOffset()) { - mNextX = mScroller.getCurrX(); - } - - if (mNextX <= 0) { - mNextX = 0; - mScroller.forceFinished(true); - } - if (mNextX >= mMaxX) { - mNextX = mMaxX; - mScroller.forceFinished(true); - } - - int dx = mCurrentX - mNextX; - - removeNonVisibleItems(dx); - fillList(dx); - positionItems(dx); - - mCurrentX = mNextX; - - if (!mScroller.isFinished()) { - post(new Runnable() { - @Override - public void run() { - requestLayout(); - } - }); - } - } - - private void fillList(final int dx) { - int edge = 0; - View child = getChildAt(getChildCount() - 1); - if (child != null) { - edge = child.getRight(); - } - fillListRight(edge, dx); - - edge = 0; - child = getChildAt(0); - if (child != null) { - edge = child.getLeft(); - } - fillListLeft(edge, dx); - } - - private void fillListRight(int rightEdge, final int dx) { - while (rightEdge + dx < getWidth() && mRightViewIndex < mAdapter.getCount()) { - - View v = null; - int type = mAdapter.getItemViewType(mRightViewIndex); - LinkedList list = mRemovedViewQueue.get(type); - if (list != null) { - v = list.poll(); - } - View child = mAdapter.getView(mRightViewIndex, v, this); - child.setTag(type); - - addAndMeasureChild(child, -1); - rightEdge += child.getMeasuredWidth(); - child.invalidate(); - - if (mRightViewIndex == mAdapter.getCount() - 1) { - mMaxX = mCurrentX + rightEdge - getWidth() + getPaddingLeft(); - } - - if (mMaxX < 0) { - mMaxX = 0; - } - mRightViewIndex++; - } - - } - - private void fillListLeft(int leftEdge, final int dx) { - while (leftEdge + dx > 0 && mLeftViewIndex >= 0) { - View v = null; - int type = mAdapter.getItemViewType(mLeftViewIndex); - LinkedList list = mRemovedViewQueue.get(type); - if (list != null) { - v = list.poll(); - } - View child = mAdapter.getView(mLeftViewIndex, v, this); - child.setTag(type); - - addAndMeasureChild(child, 0); - leftEdge -= child.getMeasuredWidth(); - mLeftViewIndex--; - mDisplayOffset -= child.getMeasuredWidth(); - } - } - - private void removeNonVisibleItems(final int dx) { - View child = getChildAt(0); - while (child != null && child.getRight() + dx <= 0) { - mDisplayOffset += child.getMeasuredWidth(); - - int type = (Integer) child.getTag(); - LinkedList list = mRemovedViewQueue.get(type); - if (list == null) { - list = new LinkedList<>(); - mRemovedViewQueue.put(type, list); - } - list.add(child); - - removeViewInLayout(child); - mLeftViewIndex++; - child = getChildAt(0); - } - - child = getChildAt(getChildCount() - 1); - while (child != null && child.getLeft() + dx >= getWidth()) { - int type = (Integer) child.getTag(); - LinkedList list = mRemovedViewQueue.get(type); - if (list == null) { - list = new LinkedList<>(); - mRemovedViewQueue.put(type, list); - } - list.add(child); - - removeViewInLayout(child); - mRightViewIndex--; - child = getChildAt(getChildCount() - 1); - } - } - - private void positionItems(final int dx) { - if (getChildCount() > 0) { - mDisplayOffset += dx; - int left = mDisplayOffset; - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - int childWidth = child.getMeasuredWidth(); - child.layout(left + getPaddingLeft(), 0, left + childWidth + getPaddingLeft(), child.getMeasuredHeight()); - left += childWidth + child.getPaddingRight(); - } - } - } - - public synchronized void scrollTo(int x) { - mScroller.startScroll(mNextX, 0, x - mNextX, 0); - requestLayout(); - } - - @Override - public boolean dispatchTouchEvent(MotionEvent ev) { - boolean handled = super.dispatchTouchEvent(ev); - handled |= mGesture.onTouchEvent(ev); - return handled; - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - requestDisallowInterceptTouchEvent(true); - return super.onInterceptTouchEvent(ev); - } - - protected boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - synchronized (HorizontalListView.this) { - mScroller.fling(mNextX, 0, (int) -velocityX, 0, 0, mMaxX, 0, 0); - } - requestLayout(); - - return true; - } - - protected boolean onDown(MotionEvent e) { - mScroller.forceFinished(true); - return true; - } - - private OnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener() { - - @Override - public boolean onDown(MotionEvent e) { - return HorizontalListView.this.onDown(e); - } - - @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - return HorizontalListView.this.onFling(e1, e2, velocityX, velocityY); - } - - @Override - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - synchronized (HorizontalListView.this) { - mNextX += (int) distanceX; - } - requestLayout(); - return true; - } - - @Override - public boolean onSingleTapUp(MotionEvent e) { - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - if (isEventWithinView(e, child)) { - if (mOnItemClicked != null) { - mOnItemClicked.onItemClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId(mLeftViewIndex + 1 + i)); - } - if (mOnItemSelected != null) { - mOnItemSelected.onItemSelected(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId(mLeftViewIndex + 1 + i)); - } - break; - } - } - return true; - } - - @Override - public void onLongPress(MotionEvent e) { - int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - View child = getChildAt(i); - if (isEventWithinView(e, child)) { - if (mOnItemLongClicked != null) { - mOnItemLongClicked.onItemLongClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId(mLeftViewIndex + 1 + i)); - } - break; - } - } - } - - private boolean isEventWithinView(MotionEvent e, View child) { - Rect viewRect = new Rect(); - int[] childPosition = new int[2]; - child.getLocationOnScreen(childPosition); - int left = childPosition[0]; - int right = left + child.getWidth(); - int top = childPosition[1]; - int bottom = top + child.getHeight(); - viewRect.set(left, top, right, bottom); - return viewRect.contains((int) e.getRawX(), (int) e.getRawY()); - } - }; -} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/LayoutHelper.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/LayoutHelper.java new file mode 100644 index 000000000..2446115da --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/LayoutHelper.java @@ -0,0 +1,128 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Components; + +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; + +import org.telegram.android.AndroidUtilities; + +public class LayoutHelper { + + public static final int MATCH_PARENT = -1; + public static final int WRAP_CONTENT = -2; + + private static int getSize(float size) { + return (int) (size < 0 ? size : AndroidUtilities.dp(size)); + } + + public static FrameLayout.LayoutParams createFrame(int width, float height, int gravity, float leftMargin, float topMargin, float rightMargin, float bottomMargin) { + FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(getSize(width), getSize(height), gravity); + layoutParams.setMargins(AndroidUtilities.dp(leftMargin), AndroidUtilities.dp(topMargin), AndroidUtilities.dp(rightMargin), AndroidUtilities.dp(bottomMargin)); + return layoutParams; + } + + public static FrameLayout.LayoutParams createFrame(int width, int height, int gravity) { + return new FrameLayout.LayoutParams(getSize(width), getSize(height), gravity); + } + + public static FrameLayout.LayoutParams createFrame(int width, float height) { + return new FrameLayout.LayoutParams(getSize(width), getSize(height)); + } + + public static RelativeLayout.LayoutParams createRelative(float width, float height, int leftMargin, int topMargin, int rightMargin, int bottomMargin, int alignParent, int alignRelative, int anchorRelative) { + RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(getSize(width), getSize(height)); + if (alignParent >= 0) { + layoutParams.addRule(alignParent); + } + if (alignRelative >= 0 && anchorRelative >= 0) { + layoutParams.addRule(alignRelative, anchorRelative); + } + layoutParams.leftMargin = AndroidUtilities.dp(leftMargin); + layoutParams.topMargin = AndroidUtilities.dp(topMargin); + layoutParams.rightMargin = AndroidUtilities.dp(rightMargin); + layoutParams.bottomMargin = AndroidUtilities.dp(bottomMargin); + return layoutParams; + } + + public static RelativeLayout.LayoutParams createRelative(int width, int height, int leftMargin, int topMargin, int rightMargin, int bottomMargin) { + return createRelative(width, height, leftMargin, topMargin, rightMargin, bottomMargin, -1, -1, -1); + } + + public static RelativeLayout.LayoutParams createRelative(int width, int height, int leftMargin, int topMargin, int rightMargin, int bottomMargin, int alignParent) { + return createRelative(width, height, leftMargin, topMargin, rightMargin, bottomMargin, alignParent, -1, -1); + } + + public static RelativeLayout.LayoutParams createRelative(float width, float height, int leftMargin, int topMargin, int rightMargin, int bottomMargin, int alignRelative, int anchorRelative) { + return createRelative(width, height, leftMargin, topMargin, rightMargin, bottomMargin, -1, alignRelative, anchorRelative); + } + + public static RelativeLayout.LayoutParams createRelative(int width, int height, int alignParent, int alignRelative, int anchorRelative) { + return createRelative(width, height, 0, 0, 0, 0, alignParent, alignRelative, anchorRelative); + } + + public static RelativeLayout.LayoutParams createRelative(int width, int height) { + return createRelative(width, height, 0, 0, 0, 0, -1, -1, -1); + } + + public static RelativeLayout.LayoutParams createRelative(int width, int height, int alignParent) { + return createRelative(width, height, 0, 0, 0, 0, alignParent, -1, -1); + } + + public static RelativeLayout.LayoutParams createRelative(int width, int height, int alignRelative, int anchorRelative) { + return createRelative(width, height, 0, 0, 0, 0, -1, alignRelative, anchorRelative); + } + + public static LinearLayout.LayoutParams createLinear(int width, int height, float weight, int gravity, int leftMargin, int topMargin, int rightMargin, int bottomMargin) { + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(getSize(width), getSize(height), weight); + layoutParams.setMargins(AndroidUtilities.dp(leftMargin), AndroidUtilities.dp(topMargin), AndroidUtilities.dp(rightMargin), AndroidUtilities.dp(bottomMargin)); + layoutParams.gravity = gravity; + return layoutParams; + } + + public static LinearLayout.LayoutParams createLinear(int width, int height, float weight, int leftMargin, int topMargin, int rightMargin, int bottomMargin) { + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(getSize(width), getSize(height), weight); + layoutParams.setMargins(AndroidUtilities.dp(leftMargin), AndroidUtilities.dp(topMargin), AndroidUtilities.dp(rightMargin), AndroidUtilities.dp(bottomMargin)); + return layoutParams; + } + + public static LinearLayout.LayoutParams createLinear(int width, int height, int gravity, int leftMargin, int topMargin, int rightMargin, int bottomMargin) { + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(getSize(width), getSize(height)); + layoutParams.setMargins(AndroidUtilities.dp(leftMargin), AndroidUtilities.dp(topMargin), AndroidUtilities.dp(rightMargin), AndroidUtilities.dp(bottomMargin)); + layoutParams.gravity = gravity; + return layoutParams; + } + + public static LinearLayout.LayoutParams createLinear(int width, int height, int leftMargin, int topMargin, int rightMargin, int bottomMargin) { + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(getSize(width), getSize(height)); + layoutParams.setMargins(AndroidUtilities.dp(leftMargin), AndroidUtilities.dp(topMargin), AndroidUtilities.dp(rightMargin), AndroidUtilities.dp(bottomMargin)); + return layoutParams; + } + + public static LinearLayout.LayoutParams createLinear(int width, int height, float weight, int gravity) { + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(getSize(width), getSize(height), weight); + layoutParams.gravity = gravity; + return layoutParams; + } + + public static LinearLayout.LayoutParams createLinear(int width, int height, int gravity) { + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(getSize(width), getSize(height)); + layoutParams.gravity = gravity; + return layoutParams; + } + + public static LinearLayout.LayoutParams createLinear(int width, int height, float weight) { + return new LinearLayout.LayoutParams(getSize(width), getSize(height), weight); + } + + public static LinearLayout.LayoutParams createLinear(int width, int height) { + return new LinearLayout.LayoutParams(getSize(width), getSize(height)); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/LayoutListView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/LayoutListView.java deleted file mode 100644 index 1ed6f83b6..000000000 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/LayoutListView.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * This is the source code of Telegram for Android v. 1.3.2. - * 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. - */ - -package org.telegram.ui.Components; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; -import android.widget.ListView; - -public class LayoutListView extends ListView { - - public interface OnInterceptTouchEventListener { - boolean onInterceptTouchEvent(MotionEvent event); - } - - private OnInterceptTouchEventListener onInterceptTouchEventListener; - private int height = -1; - private int forceTop = Integer.MIN_VALUE; - - public LayoutListView(Context context) { - super(context); - } - - public LayoutListView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public LayoutListView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - public void setOnInterceptTouchEventListener(OnInterceptTouchEventListener listener) { - onInterceptTouchEventListener = listener; - } - - public void setForceTop(int value) { - forceTop = value; - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - if (onInterceptTouchEventListener != null) { - return onInterceptTouchEventListener.onInterceptTouchEvent(ev) || super.onInterceptTouchEvent(ev); - } - return super.onInterceptTouchEvent(ev); - } - - @SuppressLint("DrawAllocation") - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - View v = getChildAt(getChildCount() - 1); - int scrollTo = getLastVisiblePosition(); - if (v != null && height > 0 && changed && ((bottom - top) < height)) { - int lastTop = forceTop == Integer.MIN_VALUE ? (bottom - top) - (height - v.getTop()) - getPaddingTop() : forceTop; - forceTop = Integer.MIN_VALUE; - setSelectionFromTop(scrollTo, lastTop); - super.onLayout(changed, left, top, right, bottom); - -// post(new Runnable() { -// @Override -// public void run() { -// try { -// setSelectionFromTop(scrollTo, lastTop); -// } catch (Exception e) { -// e.printStackTrace(); -// } -// } -// }); - } else { - if (forceTop != Integer.MIN_VALUE) { - setSelectionFromTop(scrollTo, forceTop); - forceTop = Integer.MIN_VALUE; - } - try { - super.onLayout(changed, left, top, right, bottom); - } catch (Exception e) { - e.printStackTrace(); - } - } - height = (bottom - top); - } -} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/MapPlaceholderDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/MapPlaceholderDrawable.java new file mode 100644 index 000000000..7611268df --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/MapPlaceholderDrawable.java @@ -0,0 +1,72 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Components; + +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; + +import org.telegram.android.AndroidUtilities; + +public class MapPlaceholderDrawable extends Drawable { + + private Paint paint; + private Paint linePaint; + + public MapPlaceholderDrawable() { + super(); + paint = new Paint(); + paint.setColor(0xffded7d6); + linePaint = new Paint(); + linePaint.setColor(0xffc6bfbe); + linePaint.setStrokeWidth(AndroidUtilities.dp(1)); + } + + @Override + public void draw(Canvas canvas) { + canvas.drawRect(getBounds(), paint); + int gap = AndroidUtilities.dp(9); + int xcount = getBounds().width() / gap; + int ycount = getBounds().height() / gap; + int x = getBounds().left; + int y = getBounds().top; + for (int a = 0; a < xcount; a++) { + canvas.drawLine(x + gap * (a + 1), y, x + gap * (a + 1), y + getBounds().height(), linePaint); + } + for (int a = 0; a < ycount; a++) { + canvas.drawLine(x, y + gap * (a + 1), x + getBounds().width(), y + gap * (a + 1), linePaint); + } + } + + @Override + public void setAlpha(int alpha) { + + } + + @Override + public void setColorFilter(ColorFilter cf) { + + } + + @Override + public int getOpacity() { + return 0; + } + + @Override + public int getIntrinsicWidth() { + return 0; + } + + @Override + public int getIntrinsicHeight() { + return 0; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PagerSlidingTabStrip.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PagerSlidingTabStrip.java index 33b34963e..6321efc63 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PagerSlidingTabStrip.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PagerSlidingTabStrip.java @@ -8,32 +8,22 @@ package org.telegram.ui.Components; -import java.util.Locale; - -import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.Style; -import android.graphics.Typeface; import android.os.Build; -import android.os.Parcel; -import android.os.Parcelable; import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager.OnPageChangeListener; -import android.util.AttributeSet; import android.util.DisplayMetrics; -import android.util.TypedValue; -import android.view.Gravity; import android.view.View; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.widget.FrameLayout; import android.widget.HorizontalScrollView; -import android.widget.ImageButton; +import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.TextView; -import org.telegram.messenger.R; +import org.telegram.android.AndroidUtilities; public class PagerSlidingTabStrip extends HorizontalScrollView { @@ -56,77 +46,45 @@ public class PagerSlidingTabStrip extends HorizontalScrollView { private Paint rectPaint; - private int indicatorColor = 0xFF666666; - private int underlineColor = 0x1A000000; + private int indicatorColor = 0xff666666; + private int underlineColor = 0x1a000000; private boolean shouldExpand = false; - private boolean textAllCaps = true; - private int scrollOffset = 52; - private int indicatorHeight = 8; - private int underlineHeight = 2; - private int dividerPadding = 12; - private int tabPadding = 24; - - private int tabTextSize = 12; - private int tabTextColor = 0xFF666666; - private Typeface tabTypeface = null; - private int tabTypefaceStyle = Typeface.BOLD; + private int scrollOffset = AndroidUtilities.dp(52); + private int indicatorHeight = AndroidUtilities.dp(8); + private int underlineHeight = AndroidUtilities.dp(2); + private int dividerPadding = AndroidUtilities.dp(12); + private int tabPadding = AndroidUtilities.dp(24); private int lastScrollX = 0; - private int tabBackgroundResId = R.drawable.background_tab; - - private Locale locale; - public PagerSlidingTabStrip(Context context) { - this(context, null); - } - - public PagerSlidingTabStrip(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public PagerSlidingTabStrip(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); + super(context); setFillViewport(true); setWillNotDraw(false); tabsContainer = new LinearLayout(context); tabsContainer.setOrientation(LinearLayout.HORIZONTAL); - tabsContainer.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); + tabsContainer.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); addView(tabsContainer); DisplayMetrics dm = getResources().getDisplayMetrics(); - scrollOffset = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, scrollOffset, dm); - indicatorHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, indicatorHeight, dm); - underlineHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, underlineHeight, dm); - dividerPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dividerPadding, dm); - tabPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, tabPadding, dm); - tabTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, tabTextSize, dm); - rectPaint = new Paint(); rectPaint.setAntiAlias(true); rectPaint.setStyle(Style.FILL); - defaultTabLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); - - if (locale == null) { - locale = getResources().getConfiguration().locale; - } + defaultTabLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutHelper.MATCH_PARENT); } public void setViewPager(ViewPager pager) { this.pager = pager; - if (pager.getAdapter() == null) { throw new IllegalStateException("ViewPager does not have adapter instance."); } - pager.setOnPageChangeListener(pageListener); - notifyDataSetChanged(); } @@ -135,128 +93,62 @@ public class PagerSlidingTabStrip extends HorizontalScrollView { } public void notifyDataSetChanged() { - tabsContainer.removeAllViews(); - tabCount = pager.getAdapter().getCount(); - for (int i = 0; i < tabCount; i++) { - if (pager.getAdapter() instanceof IconTabProvider) { addIconTab(i, ((IconTabProvider) pager.getAdapter()).getPageIconResId(i)); - } else { - addTextTab(i, pager.getAdapter().getPageTitle(i).toString()); } - } - updateTabStyles(); - getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { - - @SuppressWarnings("deprecation") - @SuppressLint("NewApi") @Override public void onGlobalLayout() { - if (Build.VERSION.SDK_INT < 16) { getViewTreeObserver().removeGlobalOnLayoutListener(this); } else { getViewTreeObserver().removeOnGlobalLayoutListener(this); } - currentPosition = pager.getCurrentItem(); scrollToChild(currentPosition, 0); } }); - - updateExpanded(); - - } - - private void addTextTab(final int position, String title) { - - TextView tab = new TextView(getContext()); - tab.setText(title); - tab.setFocusable(true); - tab.setGravity(Gravity.CENTER); - tab.setSingleLine(); - - tab.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - pager.setCurrentItem(position); - } - }); - - tabsContainer.addView(tab); - } private void addIconTab(final int position, int resId) { - - ImageButton tab = new ImageButton(getContext()); + ImageView tab = new ImageView(getContext()); tab.setFocusable(true); tab.setImageResource(resId); - + tab.setScaleType(ImageView.ScaleType.CENTER); tab.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { pager.setCurrentItem(position); } }); - tabsContainer.addView(tab); tab.setSelected(position == currentPosition); } - private void updateExpanded() { - - } - private void updateTabStyles() { - for (int i = 0; i < tabCount; i++) { - View v = tabsContainer.getChildAt(i); - v.setLayoutParams(defaultTabLayoutParams); - v.setBackgroundResource(tabBackgroundResId); if (shouldExpand) { v.setPadding(0, 0, 0, 0); v.setLayoutParams(new LinearLayout.LayoutParams(-1, -1, 1.0F)); } else { v.setPadding(tabPadding, 0, tabPadding, 0); } - - if (v instanceof TextView) { - - TextView tab = (TextView) v; - tab.setTextSize(TypedValue.COMPLEX_UNIT_PX, tabTextSize); - tab.setTypeface(tabTypeface, tabTypefaceStyle); - tab.setTextColor(tabTextColor); - - // setAllCaps() is only available from API 14, so the upper case is made manually if we are on a - // pre-ICS-build - if (textAllCaps) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - tab.setAllCaps(true); - } else { - tab.setText(tab.getText().toString().toUpperCase(locale)); - } - } - } } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); - if (!shouldExpand || MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.UNSPECIFIED) { return; } - int myWidth = getMeasuredWidth(); tabsContainer.measure(MeasureSpec.EXACTLY | myWidth, heightMeasureSpec); } @@ -265,13 +157,10 @@ public class PagerSlidingTabStrip extends HorizontalScrollView { if (tabCount == 0) { return; } - int newScrollX = tabsContainer.getChildAt(position).getLeft() + offset; - if (position > 0 || offset > 0) { newScrollX -= scrollOffset; } - if (newScrollX != lastScrollX) { lastScrollX = newScrollX; scrollTo(newScrollX, 0); @@ -288,9 +177,9 @@ public class PagerSlidingTabStrip extends HorizontalScrollView { final int height = getHeight(); - // draw indicator line - - rectPaint.setColor(indicatorColor); + // draw underline + rectPaint.setColor(underlineColor); + canvas.drawRect(0, height - underlineHeight, tabsContainer.getWidth(), height, rectPaint); // default: line below current tab View currentTab = tabsContainer.getChildAt(currentPosition); @@ -308,26 +197,19 @@ public class PagerSlidingTabStrip extends HorizontalScrollView { lineRight = (currentPositionOffset * nextTabRight + (1f - currentPositionOffset) * lineRight); } + // draw indicator line + rectPaint.setColor(indicatorColor); canvas.drawRect(lineLeft, height - indicatorHeight, lineRight, height, rectPaint); - - // draw underline - - rectPaint.setColor(underlineColor); - canvas.drawRect(0, height - underlineHeight, tabsContainer.getWidth(), height, rectPaint); } private class PageListener implements OnPageChangeListener { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - currentPosition = position; currentPositionOffset = positionOffset; - scrollToChild(position, (int) (positionOffset * tabsContainer.getChildAt(position).getWidth())); - invalidate(); - if (delegatePageListener != null) { delegatePageListener.onPageScrolled(position, positionOffset, positionOffsetPixels); } @@ -338,7 +220,6 @@ public class PagerSlidingTabStrip extends HorizontalScrollView { if (state == ViewPager.SCROLL_STATE_IDLE) { scrollToChild(pager.getCurrentItem(), 0); } - if (delegatePageListener != null) { delegatePageListener.onPageScrollStateChanged(state); } @@ -353,7 +234,6 @@ public class PagerSlidingTabStrip extends HorizontalScrollView { tabsContainer.getChildAt(a).setSelected(a == position); } } - } public void onSizeChanged(int paramInt1, int paramInt2, int paramInt3, int paramInt4) { @@ -441,51 +321,6 @@ public class PagerSlidingTabStrip extends HorizontalScrollView { return shouldExpand; } - public boolean isTextAllCaps() { - return textAllCaps; - } - - public void setAllCaps(boolean textAllCaps) { - this.textAllCaps = textAllCaps; - } - - public void setTextSize(int textSizePx) { - this.tabTextSize = textSizePx; - updateTabStyles(); - } - - public int getTextSize() { - return tabTextSize; - } - - public void setTextColor(int textColor) { - this.tabTextColor = textColor; - updateTabStyles(); - } - - public void setTextColorResource(int resId) { - this.tabTextColor = getResources().getColor(resId); - updateTabStyles(); - } - - public int getTextColor() { - return tabTextColor; - } - - public void setTypeface(Typeface typeface, int style) { - this.tabTypeface = typeface; - this.tabTypefaceStyle = style; - updateTabStyles(); - } - - public void setTabBackground(int resId) { - this.tabBackgroundResId = resId; - } - - public int getTabBackground() { - return tabBackgroundResId; - } - public void setTabPaddingLeftRight(int paddingPx) { this.tabPadding = paddingPx; updateTabStyles(); @@ -494,51 +329,4 @@ public class PagerSlidingTabStrip extends HorizontalScrollView { public int getTabPaddingLeftRight() { return tabPadding; } - - @Override - public void onRestoreInstanceState(Parcelable state) { - SavedState savedState = (SavedState) state; - super.onRestoreInstanceState(savedState.getSuperState()); - currentPosition = savedState.currentPosition; - requestLayout(); - } - - @Override - public Parcelable onSaveInstanceState() { - Parcelable superState = super.onSaveInstanceState(); - SavedState savedState = new SavedState(superState); - savedState.currentPosition = currentPosition; - return savedState; - } - - static class SavedState extends BaseSavedState { - int currentPosition; - - public SavedState(Parcelable superState) { - super(superState); - } - - private SavedState(Parcel in) { - super(in); - currentPosition = in.readInt(); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeInt(currentPosition); - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - @Override - public SavedState createFromParcel(Parcel in) { - return new SavedState(in); - } - - @Override - public SavedState[] newArray(int size) { - return new SavedState[size]; - } - }; - } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PasscodeView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PasscodeView.java index e314602c3..22f7a4326 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PasscodeView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PasscodeView.java @@ -38,7 +38,6 @@ import android.view.inputmethod.EditorInfo; import android.widget.EditText; import android.widget.FrameLayout; import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.TextView; import org.telegram.android.AndroidUtilities; @@ -48,11 +47,10 @@ import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.FileLog; import org.telegram.messenger.R; import org.telegram.messenger.UserConfig; -import org.telegram.messenger.Utilities; -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 org.telegram.android.AnimationCompat.AnimatorListenerAdapterProxy; +import org.telegram.android.AnimationCompat.AnimatorSetProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.ViewProxy; import java.util.ArrayList; import java.util.Locale; @@ -424,15 +422,15 @@ public class PasscodeView extends FrameLayout { backgroundFrameLayout = new FrameLayout(context); addView(backgroundFrameLayout); LayoutParams layoutParams = (LayoutParams) backgroundFrameLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; backgroundFrameLayout.setLayoutParams(layoutParams); passwordFrameLayout = new FrameLayout(context); addView(passwordFrameLayout); layoutParams = (LayoutParams) passwordFrameLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; passwordFrameLayout.setLayoutParams(layoutParams); @@ -458,8 +456,8 @@ public class PasscodeView extends FrameLayout { passcodeTextView.setGravity(Gravity.CENTER_HORIZONTAL); passwordFrameLayout.addView(passcodeTextView); layoutParams = (LayoutParams) passcodeTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.bottomMargin = AndroidUtilities.dp(62); layoutParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; passcodeTextView.setLayoutParams(layoutParams); @@ -467,8 +465,8 @@ public class PasscodeView extends FrameLayout { passwordEditText2 = new AnimatingTextView(context); passwordFrameLayout.addView(passwordEditText2); layoutParams = (FrameLayout.LayoutParams) passwordEditText2.getLayoutParams(); - layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.leftMargin = AndroidUtilities.dp(70); layoutParams.rightMargin = AndroidUtilities.dp(70); layoutParams.bottomMargin = AndroidUtilities.dp(6); @@ -488,8 +486,8 @@ public class PasscodeView extends FrameLayout { AndroidUtilities.clearCursorDrawable(passwordEditText); passwordFrameLayout.addView(passwordEditText); layoutParams = (FrameLayout.LayoutParams) passwordEditText.getLayoutParams(); - layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.leftMargin = AndroidUtilities.dp(70); layoutParams.rightMargin = AndroidUtilities.dp(70); layoutParams.bottomMargin = AndroidUtilities.dp(6); @@ -571,7 +569,7 @@ public class PasscodeView extends FrameLayout { lineFrameLayout.setBackgroundColor(0x26ffffff); passwordFrameLayout.addView(lineFrameLayout); layoutParams = (LayoutParams) lineFrameLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(1); layoutParams.gravity = Gravity.BOTTOM | Gravity.LEFT; layoutParams.leftMargin = AndroidUtilities.dp(20); @@ -581,8 +579,8 @@ public class PasscodeView extends FrameLayout { numbersFrameLayout = new FrameLayout(context); addView(numbersFrameLayout); layoutParams = (LayoutParams) numbersFrameLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; numbersFrameLayout.setLayoutParams(layoutParams); @@ -676,55 +674,42 @@ public class PasscodeView extends FrameLayout { int key = KeyEvent.KEYCODE_DEL; switch (tag) { case 0: - key = KeyEvent.KEYCODE_0; passwordEditText2.appendCharacter("0"); break; case 1: - key = KeyEvent.KEYCODE_1; passwordEditText2.appendCharacter("1"); break; case 2: - key = KeyEvent.KEYCODE_2; passwordEditText2.appendCharacter("2"); break; case 3: - key = KeyEvent.KEYCODE_3; passwordEditText2.appendCharacter("3"); break; case 4: - key = KeyEvent.KEYCODE_4; passwordEditText2.appendCharacter("4"); break; case 5: - key = KeyEvent.KEYCODE_5; passwordEditText2.appendCharacter("5"); break; case 6: - key = KeyEvent.KEYCODE_6; passwordEditText2.appendCharacter("6"); break; case 7: - key = KeyEvent.KEYCODE_7; passwordEditText2.appendCharacter("7"); break; case 8: - key = KeyEvent.KEYCODE_8; passwordEditText2.appendCharacter("8"); break; case 9: - key = KeyEvent.KEYCODE_9; passwordEditText2.appendCharacter("9"); break; case 10: - key = KeyEvent.KEYCODE_DEL; passwordEditText2.eraseLastCharacter(); break; } if (passwordEditText2.lenght() == 4) { processDone(); } - //passwordEditText.dispatchKeyEvent(new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, key, 0)); - //passwordEditText.dispatchKeyEvent(new KeyEvent(0, 0, KeyEvent.ACTION_UP, key, 0)); } }); numberFrameLayouts.add(frameLayout); @@ -755,7 +740,7 @@ public class PasscodeView extends FrameLayout { onPasscodeError(); return; } - if (!Utilities.MD5(password).equals(UserConfig.passcodeHash)) { + if (!UserConfig.checkPasscode(password)) { passwordEditText.setText(""); passwordEditText2.eraseAllCharacters(true); onPasscodeError(); @@ -867,7 +852,6 @@ public class PasscodeView extends FrameLayout { backgroundDrawable = ApplicationLoader.getCachedWallpaper(); if (backgroundDrawable != null) { backgroundFrameLayout.setBackgroundColor(0xbf000000); - customTheme = true; } else { backgroundFrameLayout.setBackgroundColor(0xff517c9e); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoFilterView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoFilterView.java index da9ac0662..ced235b6a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoFilterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoFilterView.java @@ -8,6 +8,7 @@ package org.telegram.ui.Components; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -18,8 +19,6 @@ import android.opengl.GLES20; import android.opengl.GLUtils; import android.os.Build; import android.os.Looper; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.util.TypedValue; import android.view.Gravity; import android.view.MotionEvent; @@ -32,14 +31,16 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; +import org.telegram.android.support.widget.LinearLayoutManager; +import org.telegram.android.support.widget.RecyclerView; import org.telegram.messenger.DispatchQueue; import org.telegram.messenger.FileLog; import org.telegram.messenger.R; import org.telegram.messenger.Utilities; -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 org.telegram.android.AnimationCompat.AnimatorListenerAdapterProxy; +import org.telegram.android.AnimationCompat.AnimatorSetProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.ViewProxy; import org.telegram.ui.Cells.PhotoEditToolCell; import java.nio.ByteBuffer; @@ -55,6 +56,7 @@ import javax.microedition.khronos.egl.EGLSurface; import javax.microedition.khronos.opengles.GL; import javax.microedition.khronos.opengles.GL10; +@SuppressLint("NewApi") public class PhotoFilterView extends FrameLayout { private boolean showOriginal; @@ -1333,8 +1335,8 @@ public class PhotoFilterView extends FrameLayout { addView(textureView); textureView.setVisibility(INVISIBLE); LayoutParams layoutParams = (LayoutParams) textureView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; textureView.setLayoutParams(layoutParams); textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { @@ -1380,8 +1382,8 @@ public class PhotoFilterView extends FrameLayout { blurControl.setVisibility(INVISIBLE); addView(blurControl); layoutParams = (LayoutParams) blurControl.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.LEFT | Gravity.TOP; blurControl.setLayoutParams(layoutParams); blurControl.setDelegate(new PhotoFilterBlurControl.PhotoFilterLinearBlurControlDelegate() { @@ -1400,7 +1402,7 @@ public class PhotoFilterView extends FrameLayout { toolsView = new FrameLayout(context); addView(toolsView); layoutParams = (LayoutParams) toolsView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(126); layoutParams.gravity = Gravity.LEFT | Gravity.BOTTOM; toolsView.setLayoutParams(layoutParams); @@ -1409,7 +1411,7 @@ public class PhotoFilterView extends FrameLayout { frameLayout.setBackgroundColor(0xff1a1a1a); toolsView.addView(frameLayout); layoutParams = (LayoutParams) frameLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(48); layoutParams.gravity = Gravity.BOTTOM | Gravity.LEFT; frameLayout.setLayoutParams(layoutParams); @@ -1424,8 +1426,8 @@ public class PhotoFilterView extends FrameLayout { cancelTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); frameLayout.addView(cancelTextView); layoutParams = (LayoutParams) cancelTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; cancelTextView.setLayoutParams(layoutParams); @@ -1439,8 +1441,8 @@ public class PhotoFilterView extends FrameLayout { doneTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); frameLayout.addView(doneTextView); layoutParams = (LayoutParams) doneTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.RIGHT; doneTextView.setLayoutParams(layoutParams); @@ -1455,68 +1457,68 @@ public class PhotoFilterView extends FrameLayout { recyclerListView.setAdapter(toolsAdapter = new ToolsAdapter(context)); toolsView.addView(recyclerListView); layoutParams = (FrameLayout.LayoutParams) recyclerListView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(60); layoutParams.gravity = Gravity.LEFT | Gravity.TOP; recyclerListView.setLayoutParams(layoutParams); - recyclerListView.addOnItemTouchListener(new RecyclerListView.RecyclerListViewItemClickListener(context, new RecyclerListView.OnItemClickListener() { + recyclerListView.setOnItemClickListener(new RecyclerListView.OnItemClickListener() { @Override - public void onItemClick(View view, int i) { - selectedTool = i; - if (i == enhanceTool) { + public void onItemClick(View view, int position) { + selectedTool = position; + if (position == enhanceTool) { previousValue = enhanceValue; valueSeekBar.setMinMax(0, 100); paramTextView.setText(LocaleController.getString("Enhance", R.string.Enhance)); - } else if (i == highlightsTool) { + } else if (position == highlightsTool) { previousValue = highlightsValue; valueSeekBar.setMinMax(0, 100); paramTextView.setText(LocaleController.getString("Highlights", R.string.Highlights)); - } else if (i == contrastTool) { + } else if (position == contrastTool) { previousValue = contrastValue; valueSeekBar.setMinMax(-100, 100); paramTextView.setText(LocaleController.getString("Contrast", R.string.Contrast)); - } else if (i == exposureTool) { + } else if (position == exposureTool) { previousValue = exposureValue; valueSeekBar.setMinMax(-100, 100); paramTextView.setText(LocaleController.getString("Exposure", R.string.Exposure)); - } else if (i == warmthTool) { + } else if (position == warmthTool) { previousValue = warmthValue; valueSeekBar.setMinMax(-100, 100); paramTextView.setText(LocaleController.getString("Warmth", R.string.Warmth)); - } else if (i == saturationTool) { + } else if (position == saturationTool) { previousValue = saturationValue; valueSeekBar.setMinMax(-100, 100); paramTextView.setText(LocaleController.getString("Saturation", R.string.Saturation)); - } else if (i == vignetteTool) { + } else if (position == vignetteTool) { previousValue = vignetteValue; valueSeekBar.setMinMax(0, 100); paramTextView.setText(LocaleController.getString("Vignette", R.string.Vignette)); - } else if (i == shadowsTool) { + } else if (position == shadowsTool) { previousValue = shadowsValue; valueSeekBar.setMinMax(0, 100); paramTextView.setText(LocaleController.getString("Shadows", R.string.Shadows)); - } else if (i == grainTool) { + } else if (position == grainTool) { previousValue = grainValue; valueSeekBar.setMinMax(0, 100); paramTextView.setText(LocaleController.getString("Grain", R.string.Grain)); - } else if (i == sharpenTool) { + } else if (position == sharpenTool) { previousValue = sharpenValue; valueSeekBar.setMinMax(0, 100); paramTextView.setText(LocaleController.getString("Sharpen", R.string.Sharpen)); - } else if (i == blurTool) { + } else if (position == blurTool) { previousValue = blurType; } valueSeekBar.setProgress((int) previousValue, false); updateValueTextView(); switchToOrFromEditMode(); } - })); + }); editView = new FrameLayout(context); editView.setVisibility(GONE); addView(editView); layoutParams = (LayoutParams) editView.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(126); layoutParams.gravity = Gravity.LEFT | Gravity.BOTTOM; editView.setLayoutParams(layoutParams); @@ -1525,7 +1527,7 @@ public class PhotoFilterView extends FrameLayout { frameLayout.setBackgroundColor(0xff1a1a1a); editView.addView(frameLayout); layoutParams = (LayoutParams) frameLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(48); layoutParams.gravity = Gravity.BOTTOM | Gravity.LEFT; frameLayout.setLayoutParams(layoutParams); @@ -1536,8 +1538,8 @@ public class PhotoFilterView extends FrameLayout { imageView.setPadding(AndroidUtilities.dp(22), 0, AndroidUtilities.dp(22), 0); frameLayout.addView(imageView); layoutParams = (LayoutParams) imageView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; imageView.setLayoutParams(layoutParams); imageView.setOnClickListener(new OnClickListener() { @@ -1579,8 +1581,8 @@ public class PhotoFilterView extends FrameLayout { imageView.setPadding(AndroidUtilities.dp(22), AndroidUtilities.dp(1), AndroidUtilities.dp(22), 0); frameLayout.addView(imageView); layoutParams = (LayoutParams) imageView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.RIGHT; imageView.setLayoutParams(layoutParams); imageView.setOnClickListener(new OnClickListener() { @@ -1597,8 +1599,8 @@ public class PhotoFilterView extends FrameLayout { blurTextView.setText(LocaleController.getString("Blur", R.string.Blur)); frameLayout.addView(blurTextView); layoutParams = (LayoutParams) blurTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER_HORIZONTAL; layoutParams.topMargin = AndroidUtilities.dp(9); blurTextView.setLayoutParams(layoutParams); @@ -1608,8 +1610,8 @@ public class PhotoFilterView extends FrameLayout { paramTextView.setTextColor(0xff808080); frameLayout.addView(paramTextView); layoutParams = (LayoutParams) paramTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER_HORIZONTAL; layoutParams.topMargin = AndroidUtilities.dp(26); paramTextView.setLayoutParams(layoutParams); @@ -1619,8 +1621,8 @@ public class PhotoFilterView extends FrameLayout { valueTextView.setTextColor(0xffffffff); frameLayout.addView(valueTextView); layoutParams = (LayoutParams) valueTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER_HORIZONTAL; layoutParams.topMargin = AndroidUtilities.dp(3); valueTextView.setLayoutParams(layoutParams); @@ -1667,7 +1669,7 @@ public class PhotoFilterView extends FrameLayout { layoutParams.width = AndroidUtilities.dp(498); layoutParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP; } else { - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.LEFT | Gravity.TOP; } valueSeekBar.setLayoutParams(layoutParams); @@ -1925,8 +1927,8 @@ public class PhotoFilterView extends FrameLayout { viewWidth -= AndroidUtilities.dp(28); viewHeight -= AndroidUtilities.dp(14 + 140); - float bitmapW = bitmapToEdit.getWidth(); - float bitmapH = bitmapToEdit.getHeight(); + float bitmapW; + float bitmapH; if (orientation == 90 || orientation == 270) { bitmapW = bitmapToEdit.getHeight(); bitmapH = bitmapToEdit.getWidth(); @@ -1966,7 +1968,7 @@ public class PhotoFilterView extends FrameLayout { layoutParams.width = total; layoutParams.leftMargin = (viewWidth - total) / 2; } else { - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.leftMargin = 0; } recyclerListView.setLayoutParams(layoutParams); @@ -2020,7 +2022,7 @@ public class PhotoFilterView extends FrameLayout { if (parameterValue > 0) { parameterValue *= 1.05f; } - return parameterValue += 1; + return parameterValue + 1; } public FrameLayout getToolsView() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoPickerBottomLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoPickerBottomLayout.java index fb6db0a09..67c72c127 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoPickerBottomLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoPickerBottomLayout.java @@ -41,8 +41,8 @@ public class PhotoPickerBottomLayout extends FrameLayout { cancelButton.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); addView(cancelButton); LayoutParams layoutParams = (LayoutParams) cancelButton.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; cancelButton.setLayoutParams(layoutParams); @@ -52,8 +52,8 @@ public class PhotoPickerBottomLayout extends FrameLayout { doneButton.setPadding(AndroidUtilities.dp(29), 0, AndroidUtilities.dp(29), 0); addView(doneButton); layoutParams = (LayoutParams) doneButton.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.RIGHT; doneButton.setLayoutParams(layoutParams); @@ -67,7 +67,7 @@ public class PhotoPickerBottomLayout extends FrameLayout { doneButtonBadgeTextView.setPadding(AndroidUtilities.dp(8), 0, AndroidUtilities.dp(8), AndroidUtilities.dp(1)); doneButton.addView(doneButtonBadgeTextView); LinearLayout.LayoutParams layoutParams1 = (LinearLayout.LayoutParams) doneButtonBadgeTextView.getLayoutParams(); - layoutParams1.width = LayoutParams.WRAP_CONTENT; + layoutParams1.width = LayoutHelper.WRAP_CONTENT; layoutParams1.height = AndroidUtilities.dp(23); layoutParams1.rightMargin = AndroidUtilities.dp(10); layoutParams1.gravity = Gravity.CENTER_VERTICAL; @@ -82,9 +82,9 @@ public class PhotoPickerBottomLayout extends FrameLayout { doneButtonTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); doneButton.addView(doneButtonTextView); layoutParams1 = (LinearLayout.LayoutParams) doneButtonTextView.getLayoutParams(); - layoutParams1.width = LayoutParams.WRAP_CONTENT; + layoutParams1.width = LayoutHelper.WRAP_CONTENT; layoutParams1.gravity = Gravity.CENTER_VERTICAL; - layoutParams1.height = LayoutParams.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; doneButtonTextView.setLayoutParams(layoutParams1); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerCaptionEnterView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerCaptionEnterView.java new file mode 100644 index 000000000..0bf595a75 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerCaptionEnterView.java @@ -0,0 +1,610 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Components; + +import android.app.Activity; +import android.content.Context; +import android.text.Editable; +import android.text.InputFilter; +import android.text.InputType; +import android.text.TextWatcher; +import android.text.style.ImageSpan; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewTreeObserver; +import android.view.WindowManager; +import android.view.inputmethod.EditorInfo; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.PopupWindow; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import org.telegram.android.AndroidUtilities; +import org.telegram.android.Emoji; +import org.telegram.android.LocaleController; +import org.telegram.android.NotificationCenter; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.R; +import org.telegram.messenger.TLRPC; +import org.telegram.android.AnimationCompat.AnimatorSetProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; + +public class PhotoViewerCaptionEnterView extends FrameLayoutFixed implements NotificationCenter.NotificationCenterDelegate, SizeNotifierRelativeLayoutPhoto.SizeNotifierRelativeLayoutPhotoDelegate { + + public interface PhotoViewerCaptionEnterViewDelegate { + void onCaptionEnter(); + void onTextChanged(CharSequence text, boolean bigChange); + void onWindowSizeChanged(int size); + } + + private EditText messageEditText; + private PopupWindow emojiPopup; + private ImageView emojiButton; + private EmojiView emojiView; + private SizeNotifierRelativeLayoutPhoto sizeNotifierFrameLayout; + + private int framesDroped; + + private int keyboardTransitionState; + private boolean showKeyboardOnEmojiButton; + private ViewTreeObserver.OnPreDrawListener onPreDrawListener; + + private AnimatorSetProxy runningAnimation; + private AnimatorSetProxy runningAnimation2; + private ObjectAnimatorProxy runningAnimationAudio; + private int runningAnimationType; + private int audioInterfaceState; + + private int keyboardHeight; + private int keyboardHeightLand; + private boolean keyboardVisible; + + private View window; + private PhotoViewerCaptionEnterViewDelegate delegate; + private boolean wasFocus; + + public PhotoViewerCaptionEnterView(Context context, View windowView, SizeNotifierRelativeLayoutPhoto parent) { + super(context); + setBackgroundColor(0x7f000000); + setFocusable(true); + setFocusableInTouchMode(true); + + window = windowView; + sizeNotifierFrameLayout = parent; + + LinearLayout textFieldContainer = new LinearLayout(context); + textFieldContainer.setOrientation(LinearLayout.HORIZONTAL); + addView(textFieldContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 2, 0, 0, 0)); + + FrameLayoutFixed frameLayout = new FrameLayoutFixed(context); + textFieldContainer.addView(frameLayout, LayoutHelper.createLinear(0, LayoutHelper.WRAP_CONTENT, 1.0f)); + + emojiButton = new ImageView(context); + emojiButton.setImageResource(R.drawable.ic_smile_w); + emojiButton.setScaleType(ImageView.ScaleType.CENTER_INSIDE); + emojiButton.setPadding(AndroidUtilities.dp(4), AndroidUtilities.dp(1), 0, 0); + frameLayout.addView(emojiButton, LayoutHelper.createFrame(48, 48, Gravity.BOTTOM | Gravity.LEFT)); + emojiButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (showKeyboardOnEmojiButton) { + setKeyboardTransitionState(1); + showEmojiPopup(false, false); + int selection = messageEditText.getSelectionStart(); + MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0); + messageEditText.onTouchEvent(event); + event.recycle(); + messageEditText.setSelection(selection); + } else { + boolean show = emojiPopup == null || !emojiPopup.isShowing(); + if (show) { + setKeyboardTransitionState(5); + showEmojiPopup(show, true); + } else { + showEmojiPopup(show, true); + setKeyboardTransitionState(1); + } + } + } + }); + + messageEditText = new EditText(context); + messageEditText.setHint(LocaleController.getString("AddCaption", R.string.AddCaption)); + messageEditText.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI | EditorInfo.IME_ACTION_DONE); + messageEditText.setInputType(EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + messageEditText.setSingleLine(false); + messageEditText.setMaxLines(4); + messageEditText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); + messageEditText.setGravity(Gravity.BOTTOM); + messageEditText.setPadding(0, AndroidUtilities.dp(11), 0, AndroidUtilities.dp(12)); + messageEditText.setBackgroundDrawable(null); + AndroidUtilities.clearCursorDrawable(messageEditText); + messageEditText.setTextColor(0xffffffff); + messageEditText.setHintTextColor(0xb2ffffff); + InputFilter[] inputFilters = new InputFilter[1]; + inputFilters[0] = new InputFilter.LengthFilter(140); + messageEditText.setFilters(inputFilters); + frameLayout.addView(messageEditText, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.BOTTOM, 52, 0, 6, 0)); + messageEditText.setOnKeyListener(new OnKeyListener() { + @Override + public boolean onKey(View view, int i, KeyEvent keyEvent) { + if (i == 4 && !keyboardVisible && emojiPopup != null && emojiPopup.isShowing()) { + if (keyEvent.getAction() == 1) { + showEmojiPopup(false, true); + } + return true; + } else if (i == KeyEvent.KEYCODE_ENTER && keyEvent.getAction() == KeyEvent.ACTION_DOWN) { + delegate.onCaptionEnter(); + return true; + } + return false; + } + }); + messageEditText.setOnFocusChangeListener(new OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (!wasFocus) { + setKeyboardTransitionState(3); + } + wasFocus = hasFocus; + } + }); + messageEditText.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + if (emojiPopup != null && emojiPopup.isShowing()) { + setKeyboardTransitionState(1); + showEmojiPopup(false, false); + } else { + setKeyboardTransitionState(3); + } + } + }); + messageEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) { + if (i == EditorInfo.IME_ACTION_DONE) { + delegate.onCaptionEnter(); + return true; + } else if (keyEvent != null && i == EditorInfo.IME_NULL && keyEvent.getAction() == KeyEvent.ACTION_DOWN) { + delegate.onCaptionEnter(); + return true; + } + return false; + } + }); + messageEditText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { + + } + + @Override + public void onTextChanged(CharSequence charSequence, int start, int before, int count) { + String message = getTrimmedString(charSequence.toString()); + + if (delegate != null) { + delegate.onTextChanged(charSequence, before > count || count > 2); + } + } + + @Override + public void afterTextChanged(Editable editable) { + int i = 0; + ImageSpan[] arrayOfImageSpan = editable.getSpans(0, editable.length(), ImageSpan.class); + int j = arrayOfImageSpan.length; + while (true) { + if (i >= j) { + Emoji.replaceEmoji(editable, messageEditText.getPaint().getFontMetricsInt(), AndroidUtilities.dp(20)); + return; + } + editable.removeSpan(arrayOfImageSpan[i]); + i++; + } + } + }); + } + + private void setKeyboardTransitionState(int state) { + if (AndroidUtilities.usingHardwareInput) { + if (state == 1) { + showEmojiPopup(false, false); + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams(); + layoutParams.bottomMargin = 0;//AndroidUtilities.dp(48); + setLayoutParams(layoutParams); + keyboardTransitionState = 0; + } else if (state == 2) { + int currentHeight = AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y ? keyboardHeightLand : keyboardHeight; + sizeNotifierFrameLayout.setPadding(0, 0, 0, currentHeight); + keyboardTransitionState = 0; + } else if (state == 3) { + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams(); + layoutParams.bottomMargin = 0;//AndroidUtilities.dp(48); + setLayoutParams(layoutParams); + keyboardTransitionState = 0; + } else if (state == 4) { + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams(); + layoutParams.bottomMargin = -AndroidUtilities.dp(400); + setLayoutParams(layoutParams); + keyboardTransitionState = 0; + } else if (state == 5) { + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams(); + layoutParams.bottomMargin = 0; + setLayoutParams(layoutParams); + keyboardTransitionState = 0; + } + } else { + framesDroped = 0; + keyboardTransitionState = state; + if (state == 1) { + sizeNotifierFrameLayout.setPadding(0, 0, 0, 0); + } + } + } + + public int getKeyboardTransitionState() { + return keyboardTransitionState; + } + + private void onWindowSizeChanged(int size) { + if (delegate != null) { + delegate.onWindowSizeChanged(size); + } + } + + public void onCreate() { + NotificationCenter.getInstance().addObserver(this, NotificationCenter.emojiDidLoaded); + NotificationCenter.getInstance().addObserver(this, NotificationCenter.hideEmojiKeyboard); + sizeNotifierFrameLayout.getViewTreeObserver().addOnPreDrawListener(onPreDrawListener = new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + if (keyboardTransitionState == 1) { + if (keyboardVisible || framesDroped >= 60) { + showEmojiPopup(false, false); + keyboardTransitionState = 0; + } else { + if (messageEditText != null) { + messageEditText.requestFocus(); + AndroidUtilities.showKeyboard(messageEditText); + } + } + framesDroped++; + return false; + } else if (keyboardTransitionState == 2) { + if (!keyboardVisible || framesDroped >= 60) { + int currentHeight = AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y ? keyboardHeightLand : keyboardHeight; + sizeNotifierFrameLayout.setPadding(0, 0, 0, currentHeight); + keyboardTransitionState = 0; + } + framesDroped++; + return false; + } else if (keyboardTransitionState == 3) { + if (keyboardVisible) { + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams(); + layoutParams.bottomMargin = 0;//AndroidUtilities.usingHardwareInput ? AndroidUtilities.dp(48) : 0; + setLayoutParams(layoutParams); + keyboardTransitionState = 0; + } + } else if (keyboardTransitionState == 4) { + if (!keyboardVisible && (emojiPopup == null || !emojiPopup.isShowing())) { + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams(); + layoutParams.bottomMargin = -AndroidUtilities.dp(400); + setLayoutParams(layoutParams); + keyboardTransitionState = 0; + } + } else if (keyboardTransitionState == 5) { + if (emojiPopup != null && emojiPopup.isShowing()) { + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams(); + layoutParams.bottomMargin = 0; + setLayoutParams(layoutParams); + keyboardTransitionState = 0; + } + } + return true; + } + }); + sizeNotifierFrameLayout.setDelegate(this); + } + + public void onDestroy() { + if (isEmojiPopupShowing()) { + hideEmojiPopup(); + } + if (isKeyboardVisible()) { + closeKeyboard(); + } + keyboardVisible = false; + NotificationCenter.getInstance().removeObserver(this, NotificationCenter.emojiDidLoaded); + NotificationCenter.getInstance().removeObserver(this, NotificationCenter.hideEmojiKeyboard); + if (sizeNotifierFrameLayout != null) { + sizeNotifierFrameLayout.getViewTreeObserver().removeOnPreDrawListener(onPreDrawListener); + sizeNotifierFrameLayout.setDelegate(null); + } + } + + private String getTrimmedString(String src) { + String result = src.trim(); + if (result.length() == 0) { + return result; + } + while (src.startsWith("\n")) { + src = src.substring(1); + } + while (src.endsWith("\n")) { + src = src.substring(0, src.length() - 1); + } + return src; + } + + private void showEmojiPopup(boolean show, boolean post) { + if (show) { + if (emojiPopup == null) { + emojiView = new EmojiView(false, getContext()); + emojiView.setListener(new EmojiView.Listener() { + public boolean onBackspace() { + if (messageEditText.length() == 0) { + return false; + } + messageEditText.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)); + return true; + } + + public void onEmojiSelected(String symbol) { + int i = messageEditText.getSelectionEnd(); + if (i < 0) { + i = 0; + } + try { + CharSequence localCharSequence = Emoji.replaceEmoji(symbol, messageEditText.getPaint().getFontMetricsInt(), AndroidUtilities.dp(20)); + messageEditText.setText(messageEditText.getText().insert(i, localCharSequence)); + int j = i + localCharSequence.length(); + messageEditText.setSelection(j, j); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + + public void onStickerSelected(TLRPC.Document sticker) { + + } + }); + emojiPopup = new PopupWindow(emojiView); + } + + if (keyboardHeight <= 0) { + keyboardHeight = ApplicationLoader.applicationContext.getSharedPreferences("emoji", 0).getInt("kbd_height", AndroidUtilities.dp(200)); + } + if (keyboardHeightLand <= 0) { + keyboardHeightLand = ApplicationLoader.applicationContext.getSharedPreferences("emoji", 0).getInt("kbd_height_land3", AndroidUtilities.dp(200)); + } + int currentHeight = AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y ? keyboardHeightLand : keyboardHeight; + FileLog.e("tmessages", "show emoji with height = " + currentHeight); + emojiPopup.setHeight(View.MeasureSpec.makeMeasureSpec(currentHeight, View.MeasureSpec.EXACTLY)); + if (sizeNotifierFrameLayout != null) { + emojiPopup.setWidth(View.MeasureSpec.makeMeasureSpec(AndroidUtilities.displaySize.x, View.MeasureSpec.EXACTLY)); + } + + emojiPopup.showAtLocation(window, Gravity.BOTTOM | Gravity.LEFT, 0, 0); + + if (!keyboardVisible) { + if (sizeNotifierFrameLayout != null) { + sizeNotifierFrameLayout.setPadding(0, 0, 0, currentHeight); + emojiButton.setImageResource(R.drawable.arrow_down_w); + showKeyboardOnEmojiButton = false; + onWindowSizeChanged(sizeNotifierFrameLayout.getHeight() - sizeNotifierFrameLayout.getPaddingBottom()); + } + return; + } else { + setKeyboardTransitionState(2); + AndroidUtilities.hideKeyboard(messageEditText); + } + emojiButton.setImageResource(R.drawable.ic_keyboard_w); + showKeyboardOnEmojiButton = true; + return; + } + if (emojiButton != null) { + showKeyboardOnEmojiButton = false; + emojiButton.setImageResource(R.drawable.ic_smile_w); + } + if (emojiPopup != null) { + try { + emojiPopup.dismiss(); + } catch (Exception e) { + //don't promt + } + } + if (keyboardTransitionState == 0) { + if (sizeNotifierFrameLayout != null) { + if (post) { + sizeNotifierFrameLayout.post(new Runnable() { + public void run() { + if (sizeNotifierFrameLayout != null) { + sizeNotifierFrameLayout.setPadding(0, 0, 0, 0); + onWindowSizeChanged(sizeNotifierFrameLayout.getHeight()); + } + } + }); + } else { + sizeNotifierFrameLayout.setPadding(0, 0, 0, 0); + onWindowSizeChanged(sizeNotifierFrameLayout.getHeight()); + } + } + } + } + + public void hideEmojiPopup() { + if (emojiPopup != null && emojiPopup.isShowing()) { + showEmojiPopup(false, true); + } + setKeyboardTransitionState(4); + } + + public void openKeyboard() { + setKeyboardTransitionState(3); + messageEditText.requestFocus(); + AndroidUtilities.showKeyboard(messageEditText); + } + + public void closeKeyboard() { + setKeyboardTransitionState(4); + AndroidUtilities.hideKeyboard(messageEditText); + } + + public void setDelegate(PhotoViewerCaptionEnterViewDelegate delegate) { + this.delegate = delegate; + } + + public void setFieldText(CharSequence text) { + if (messageEditText == null) { + return; + } + messageEditText.setText(text); + messageEditText.setSelection(messageEditText.getText().length()); + if (delegate != null) { + delegate.onTextChanged(messageEditText.getText(), true); + } + } + + public int getCursorPosition() { + if (messageEditText == null) { + return 0; + } + return messageEditText.getSelectionStart(); + } + + public void replaceWithText(int start, int len, String text) { + try { + StringBuilder builder = new StringBuilder(messageEditText.getText()); + builder.replace(start, start + len, text); + messageEditText.setText(builder); + if (start + text.length() <= messageEditText.length()) { + messageEditText.setSelection(start + text.length()); + } else { + messageEditText.setSelection(messageEditText.length()); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + + public void setFieldFocused(boolean focus) { + if (messageEditText == null) { + return; + } + if (focus) { + if (!messageEditText.isFocused()) { + messageEditText.postDelayed(new Runnable() { + @Override + public void run() { + if (messageEditText != null) { + try { + messageEditText.requestFocus(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + } + }, 600); + } + } else { + if (messageEditText.isFocused() && !keyboardVisible) { + messageEditText.clearFocus(); + } + } + } + + public boolean hasText() { + return messageEditText != null && messageEditText.length() > 0; + } + + public String getFieldText() { + if (messageEditText != null && messageEditText.length() > 0) { + return getTrimmedString(messageEditText.getText().toString()); + } + return null; + } + + public CharSequence getFieldCharSequence() { + return messageEditText.getText(); + } + + public boolean isEmojiPopupShowing() { + return emojiPopup != null && emojiPopup.isShowing(); + } + + public boolean isKeyboardVisible() { + return AndroidUtilities.usingHardwareInput && getLayoutParams() != null && ((RelativeLayout.LayoutParams) getLayoutParams()).bottomMargin == 0 || keyboardVisible; + } + + @Override + public void onSizeChanged(int height, boolean isWidthGreater) { + if (height > AndroidUtilities.dp(50) && keyboardVisible) { + if (isWidthGreater) { + keyboardHeightLand = height; + ApplicationLoader.applicationContext.getSharedPreferences("emoji", 0).edit().putInt("kbd_height_land3", keyboardHeightLand).commit(); + } else { + keyboardHeight = height; + ApplicationLoader.applicationContext.getSharedPreferences("emoji", 0).edit().putInt("kbd_height", keyboardHeight).commit(); + } + } + + if (emojiPopup != null && emojiPopup.isShowing()) { + int newHeight = 0; + if (isWidthGreater) { + newHeight = keyboardHeightLand; + } else { + newHeight = keyboardHeight; + } + WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) emojiPopup.getContentView().getLayoutParams(); + if (layoutParams.width != AndroidUtilities.displaySize.x || layoutParams.height != newHeight) { + layoutParams.width = AndroidUtilities.displaySize.x; + layoutParams.height = newHeight; + WindowManager wm = (WindowManager) ApplicationLoader.applicationContext.getSystemService(Activity.WINDOW_SERVICE); + if (wm != null) { + wm.updateViewLayout(emojiPopup.getContentView(), layoutParams); + if (!keyboardVisible) { + if (sizeNotifierFrameLayout != null) { + sizeNotifierFrameLayout.setPadding(0, 0, 0, layoutParams.height); + sizeNotifierFrameLayout.requestLayout(); + onWindowSizeChanged(sizeNotifierFrameLayout.getHeight() - sizeNotifierFrameLayout.getPaddingBottom()); + } + } + } + } + } + + boolean oldValue = keyboardVisible; + keyboardVisible = height > 0; + if (keyboardVisible && (sizeNotifierFrameLayout.getPaddingBottom() > 0 || keyboardTransitionState == 1)) { + setKeyboardTransitionState(1); + } else if (keyboardTransitionState != 2 && !keyboardVisible && keyboardVisible != oldValue && emojiPopup != null && emojiPopup.isShowing()) { + showEmojiPopup(false, true); + } + if (keyboardTransitionState == 0) { + onWindowSizeChanged(sizeNotifierFrameLayout.getHeight() - sizeNotifierFrameLayout.getPaddingBottom()); + } + } + + @Override + public void didReceivedNotification(int id, Object... args) { + if (id == NotificationCenter.emojiDidLoaded) { + if (emojiView != null) { + emojiView.invalidateViews(); + } + } else if (id == NotificationCenter.hideEmojiKeyboard) { + hideEmojiPopup(); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PopupAudioView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PopupAudioView.java index 3d32ec5ef..c6f7bb038 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PopupAudioView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PopupAudioView.java @@ -20,6 +20,7 @@ import android.view.SoundEffectConstants; import org.telegram.android.AndroidUtilities; import org.telegram.android.ImageLoader; import org.telegram.android.MediaController; +import org.telegram.android.MessagesController; import org.telegram.messenger.FileLoader; import org.telegram.messenger.R; import org.telegram.android.MessageObject; @@ -58,23 +59,23 @@ public class PopupAudioView extends BaseCell implements SeekBar.SeekBarDelegate, super(context); if (backgroundMediaDrawableIn == null) { backgroundMediaDrawableIn = getResources().getDrawable(R.drawable.msg_in_photo); - statesDrawable[0][0] = getResources().getDrawable(R.drawable.play1); - statesDrawable[0][1] = getResources().getDrawable(R.drawable.play1_pressed); - statesDrawable[1][0] = getResources().getDrawable(R.drawable.pause1); - statesDrawable[1][1] = getResources().getDrawable(R.drawable.pause1_pressed); - statesDrawable[2][0] = getResources().getDrawable(R.drawable.audioload1); - statesDrawable[2][1] = getResources().getDrawable(R.drawable.audioload1_pressed); - statesDrawable[3][0] = getResources().getDrawable(R.drawable.audiocancel1); - statesDrawable[3][1] = getResources().getDrawable(R.drawable.audiocancel1_pressed); + statesDrawable[0][0] = getResources().getDrawable(R.drawable.play_w2); + statesDrawable[0][1] = getResources().getDrawable(R.drawable.play_w2_pressed); + statesDrawable[1][0] = getResources().getDrawable(R.drawable.pause_w2); + statesDrawable[1][1] = getResources().getDrawable(R.drawable.pause_w2_pressed); + statesDrawable[2][0] = getResources().getDrawable(R.drawable.download_g); + statesDrawable[2][1] = getResources().getDrawable(R.drawable.download_g_pressed); + statesDrawable[3][0] = getResources().getDrawable(R.drawable.pause_g); + statesDrawable[3][1] = getResources().getDrawable(R.drawable.pause_g_pressed); - statesDrawable[4][0] = getResources().getDrawable(R.drawable.play2); - statesDrawable[4][1] = getResources().getDrawable(R.drawable.play2_pressed); - statesDrawable[5][0] = getResources().getDrawable(R.drawable.pause2); - statesDrawable[5][1] = getResources().getDrawable(R.drawable.pause2_pressed); - statesDrawable[6][0] = getResources().getDrawable(R.drawable.audioload2); - statesDrawable[6][1] = getResources().getDrawable(R.drawable.audioload2_pressed); - statesDrawable[7][0] = getResources().getDrawable(R.drawable.audiocancel2); - statesDrawable[7][1] = getResources().getDrawable(R.drawable.audiocancel2_pressed); + statesDrawable[4][0] = getResources().getDrawable(R.drawable.play_w); + statesDrawable[4][1] = getResources().getDrawable(R.drawable.play_w_pressed); + statesDrawable[5][0] = getResources().getDrawable(R.drawable.pause_w); + statesDrawable[5][1] = getResources().getDrawable(R.drawable.pause_w_pressed); + statesDrawable[6][0] = getResources().getDrawable(R.drawable.download_b); + statesDrawable[6][1] = getResources().getDrawable(R.drawable.download_b_pressed); + statesDrawable[7][0] = getResources().getDrawable(R.drawable.pause_b); + statesDrawable[7][1] = getResources().getDrawable(R.drawable.pause_b_pressed); timePaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG); timePaint.setTextSize(AndroidUtilities.dp(16)); @@ -122,13 +123,13 @@ public class PopupAudioView extends BaseCell implements SeekBar.SeekBarDelegate, return; } - seekBarX = AndroidUtilities.dp(40); + seekBarX = AndroidUtilities.dp(54); buttonX = AndroidUtilities.dp(10); timeX = getMeasuredWidth() - timeWidth - AndroidUtilities.dp(16); - seekBar.width = getMeasuredWidth() - AndroidUtilities.dp(56) - timeWidth; + seekBar.width = getMeasuredWidth() - AndroidUtilities.dp(70) - timeWidth; seekBar.height = AndroidUtilities.dp(30); - progressView.width = getMeasuredWidth() - AndroidUtilities.dp(80) - timeWidth; + progressView.width = getMeasuredWidth() - AndroidUtilities.dp(94) - timeWidth; progressView.height = AndroidUtilities.dp(30); seekBarY = AndroidUtilities.dp(13); buttonY = AndroidUtilities.dp(10); @@ -234,6 +235,10 @@ public class PopupAudioView extends BaseCell implements SeekBar.SeekBarDelegate, private void didPressedButton() { if (buttonState == 0) { boolean result = MediaController.getInstance().playAudio(currentMessageObject); + if (!currentMessageObject.isOut() && currentMessageObject.isContentUnread()) { + MessagesController.getInstance().markMessageContentAsRead(currentMessageObject.getId()); + currentMessageObject.setContentIsRead(); + } if (result) { buttonState = 1; invalidate(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/RecordStatusDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/RecordStatusDrawable.java new file mode 100644 index 000000000..b224eb3e5 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RecordStatusDrawable.java @@ -0,0 +1,110 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Components; + +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; + +import org.telegram.android.AndroidUtilities; + +public class RecordStatusDrawable extends Drawable { + + private boolean isChat = false; + private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + private long lastUpdateTime = 0; + private boolean started = false; + private RectF rect = new RectF(); + private float progress; + + public RecordStatusDrawable() { + super(); + paint.setColor(0xffd7e8f7); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(AndroidUtilities.dp(2)); + paint.setStrokeCap(Paint.Cap.ROUND); + } + + public void setIsChat(boolean value) { + isChat = value; + } + + private void update() { + long newTime = System.currentTimeMillis(); + long dt = newTime - lastUpdateTime; + lastUpdateTime = newTime; + if (dt > 50) { + dt = 50; + } + progress += dt / 300.0f; + while (progress > 1.0f) { + progress -= 1.0f; + } + invalidateSelf(); + } + + public void start() { + lastUpdateTime = System.currentTimeMillis(); + started = true; + invalidateSelf(); + } + + public void stop() { + started = false; + } + + @Override + public void draw(Canvas canvas) { + canvas.save(); + canvas.translate(0, getIntrinsicHeight() / 2 + AndroidUtilities.dp(isChat ? 1 : 2)); + for (int a = 0; a < 4; a++) { + if (a == 0) { + paint.setAlpha((int) (255 * progress)); + } else if (a == 3) { + paint.setAlpha((int) (255 * (1.0f - progress))); + } else { + paint.setAlpha(255); + } + float side = AndroidUtilities.dp(4) * a + AndroidUtilities.dp(4) * progress; + rect.set(-side, -side, side, side); + canvas.drawArc(rect, -15, 30, false, paint); + } + canvas.restore(); + if (started) { + update(); + } + } + + @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(18); + } + + @Override + public int getIntrinsicHeight() { + return AndroidUtilities.dp(14); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerListView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerListView.java index 08eea19d4..55a75b2f3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerListView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerListView.java @@ -1,59 +1,158 @@ /* - * This is the source code of Telegram for Android v. 2.0.x. + * This is the source code of Telegram for Android v. 2.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. + * Copyright Nikolai Kudashov, 2013-2015. */ package org.telegram.ui.Components; import android.content.Context; -import android.support.v7.widget.RecyclerView; -import android.util.AttributeSet; +import android.content.res.TypedArray; import android.view.GestureDetector; +import android.view.HapticFeedbackConstants; import android.view.MotionEvent; +import android.view.SoundEffectConstants; import android.view.View; +import android.view.ViewConfiguration; + +import org.telegram.android.AndroidUtilities; +import org.telegram.android.support.widget.RecyclerView; +import org.telegram.messenger.FileLog; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; public class RecyclerListView extends RecyclerView { + private OnItemClickListener onItemClickListener; + private OnItemLongClickListener onItemLongClickListener; + private RecyclerView.OnScrollListener onScrollListener; + private OnInterceptTouchListener onInterceptTouchListener; + private View emptyView; + private Runnable selectChildRunnable; + + private GestureDetector mGestureDetector; + private View currentChildView; + private int currentChildPosition; + private boolean interceptedByChild; + private boolean wasPressed; + private boolean disallowInterceptTouchEvents; + private boolean instantClick; + + private static int[] attributes; + private static boolean gotAttributes; + public interface OnItemClickListener { void onItemClick(View view, int position); } - public static class RecyclerListViewItemClickListener implements RecyclerView.OnItemTouchListener { - private OnItemClickListener mListener; + public interface OnItemLongClickListener { + void onItemClick(View view, int position); + } - GestureDetector mGestureDetector; + public interface OnInterceptTouchListener { + boolean onInterceptTouchEvent(MotionEvent event); + } - public RecyclerListViewItemClickListener(Context context, OnItemClickListener listener) { - mListener = listener; + private class RecyclerListViewItemClickListener implements RecyclerView.OnItemTouchListener { + + public RecyclerListViewItemClickListener(Context context) { mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onSingleTapUp(MotionEvent e) { + if (currentChildView != null && onItemClickListener != null) { + currentChildView.setPressed(true); + final View view = currentChildView; + if (instantClick) { + view.playSoundEffect(SoundEffectConstants.CLICK); + onItemClickListener.onItemClick(view, currentChildPosition); + } + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + if (view != null) { + view.setPressed(false); + if (!instantClick) { + view.playSoundEffect(SoundEffectConstants.CLICK); + if (onItemClickListener != null) { + onItemClickListener.onItemClick(view, currentChildPosition); + } + } + } + } + }, ViewConfiguration.getPressedStateDuration()); + + if (selectChildRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(selectChildRunnable); + selectChildRunnable = null; + currentChildView = null; + interceptedByChild = false; + } + } return true; } + + @Override + public void onLongPress(MotionEvent e) { + if (currentChildView != null && onItemLongClickListener != null) { + currentChildView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + onItemLongClickListener.onItemClick(currentChildView, currentChildPosition); + } + } }); } @Override public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) { - View childView = view.findChildViewUnder(e.getX(), e.getY()); - if (childView != null) { - if (mListener != null && mGestureDetector.onTouchEvent(e)) { - mListener.onItemClick(childView, view.getChildPosition(childView)); + int action = e.getActionMasked(); + boolean isScrollIdle = RecyclerListView.this.getScrollState() == RecyclerListView.SCROLL_STATE_IDLE; + + if ((action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN) && currentChildView == null && isScrollIdle) { + currentChildView = view.findChildViewUnder(e.getX(), e.getY()); + currentChildPosition = -1; + if (currentChildView != null) { + currentChildPosition = view.getChildPosition(currentChildView); + MotionEvent childEvent = MotionEvent.obtain(0, 0, e.getActionMasked(), e.getX() - currentChildView.getLeft(), e.getY() - currentChildView.getTop(), 0); + if (currentChildView.onTouchEvent(childEvent)) { + interceptedByChild = true; + } + childEvent.recycle(); } - /*int action = e.getAction(); - if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) { - childView.setPressed(true); - } else if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { - childView.setPressed(false); - }*/ - } else { - /*int count = view.getChildCount(); - for (int a = 0; a < count; a++) { - view.getChildAt(a).setPressed(false); - }*/ + } + + if (currentChildView != null && !interceptedByChild) { + try { + if (e != null) { + mGestureDetector.onTouchEvent(e); + } + } catch (Exception ev) { + FileLog.e("tmessages", ev); + } + } + + if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN) { + if (!interceptedByChild && currentChildView != null) { + selectChildRunnable = new Runnable() { + @Override + public void run() { + if (selectChildRunnable != null && currentChildView != null) { + currentChildView.setPressed(true); + selectChildRunnable = null; + } + } + }; + AndroidUtilities.runOnUIThread(selectChildRunnable, ViewConfiguration.getTapTimeout()); + } + } else if (currentChildView != null && (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP || action == MotionEvent.ACTION_CANCEL || !isScrollIdle)) { + if (selectChildRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(selectChildRunnable); + selectChildRunnable = null; + } + currentChildView.setPressed(false); + currentChildView = null; + interceptedByChild = false; } return false; } @@ -64,22 +163,165 @@ public class RecyclerListView extends RecyclerView { } } + private AdapterDataObserver observer = new AdapterDataObserver() { + @Override + public void onChanged() { + checkIfEmpty(); + } + + @Override + public void onItemRangeInserted(int positionStart, int itemCount) { + checkIfEmpty(); + } + + @Override + public void onItemRangeRemoved(int positionStart, int itemCount) { + checkIfEmpty(); + } + }; + + public int[] getResourceDeclareStyleableIntArray(String packageName, String name) { + try { + Field f = Class.forName(packageName + ".R$styleable").getField(name); + if (f != null) { + return (int[]) f.get(null); + } + } catch (Throwable t) { + //ignore + } + return null; + } + public RecyclerListView(Context context) { super(context); + + try { + if (!gotAttributes) { + attributes = getResourceDeclareStyleableIntArray("com.android.internal", "View"); + gotAttributes = true; + } + TypedArray a = context.getTheme().obtainStyledAttributes(attributes); + Method initializeScrollbars = android.view.View.class.getDeclaredMethod("initializeScrollbars", TypedArray.class); + initializeScrollbars.invoke(this, a); + a.recycle(); + } catch (Throwable e) { + FileLog.e("tmessages", e); + } + + super.setOnScrollListener(new OnScrollListener() { + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + if (newState != SCROLL_STATE_IDLE && currentChildView != null) { + if (selectChildRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(selectChildRunnable); + selectChildRunnable = null; + } + MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0); + try { + mGestureDetector.onTouchEvent(event); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + currentChildView.onTouchEvent(event); + event.recycle(); + currentChildView.setPressed(false); + currentChildView = null; + interceptedByChild = false; + } + if (onScrollListener != null) { + onScrollListener.onScrollStateChanged(recyclerView, newState); + } + } + + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + if (onScrollListener != null) { + onScrollListener.onScrolled(recyclerView, dx, dy); + } + } + }); + addOnItemTouchListener(new RecyclerListViewItemClickListener(context)); } - public RecyclerListView(Context context, AttributeSet attrs) { - super(context, attrs); + @Override + public void setVerticalScrollBarEnabled(boolean verticalScrollBarEnabled) { + if (attributes != null) { + super.setVerticalScrollBarEnabled(verticalScrollBarEnabled); + } } - public RecyclerListView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); + public void setOnItemClickListener(OnItemClickListener listener) { + onItemClickListener = listener; + } + + public void setOnItemLongClickListener(OnItemLongClickListener listener) { + onItemLongClickListener = listener; + } + + public void setEmptyView(View view) { + if (emptyView == view) { + return; + } + emptyView = view; + checkIfEmpty(); + } + + public View getEmptyView() { + return emptyView; + } + + public void invalidateViews() { + int count = getChildCount(); + for (int a = 0; a < count; a++) { + getChildAt(a).invalidate(); + } } @Override public boolean onInterceptTouchEvent(MotionEvent e) { - requestDisallowInterceptTouchEvent(true); - return super.onInterceptTouchEvent(e); + if (disallowInterceptTouchEvents) { + requestDisallowInterceptTouchEvent(true); + } + return onInterceptTouchListener != null && onInterceptTouchListener.onInterceptTouchEvent(e) || super.onInterceptTouchEvent(e); + } + + private void checkIfEmpty() { + if (emptyView == null || getAdapter() == null) { + return; + } + boolean emptyViewVisible = getAdapter().getItemCount() == 0; + emptyView.setVisibility(emptyViewVisible ? VISIBLE : INVISIBLE); + setVisibility(emptyViewVisible ? INVISIBLE : VISIBLE); + } + + @Override + public void setOnScrollListener(OnScrollListener listener) { + onScrollListener = listener; + } + + public void setOnInterceptTouchListener(OnInterceptTouchListener listener) { + onInterceptTouchListener = listener; + } + + public void setInstantClick(boolean value) { + instantClick = value; + } + + public void setDisallowInterceptTouchEvents(boolean value) { + disallowInterceptTouchEvents = value; + } + + @Override + public void setAdapter(Adapter adapter) { + final Adapter oldAdapter = getAdapter(); + if (oldAdapter != null) { + oldAdapter.unregisterAdapterDataObserver(observer); + } + super.setAdapter(adapter); + if (adapter != null) { + adapter.registerAdapterDataObserver(observer); + } + checkIfEmpty(); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ResourceLoader.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ResourceLoader.java new file mode 100644 index 000000000..a72c52739 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ResourceLoader.java @@ -0,0 +1,120 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Components; + +import android.content.Context; +import android.graphics.drawable.Drawable; + +import org.telegram.messenger.R; + +public class ResourceLoader { + + public static Drawable backgroundDrawableIn; + public static Drawable backgroundDrawableInSelected; + public static Drawable backgroundDrawableOut; + public static Drawable backgroundDrawableOutSelected; + public static Drawable backgroundMediaDrawableIn; + public static Drawable backgroundMediaDrawableInSelected; + public static Drawable backgroundMediaDrawableOut; + public static Drawable backgroundMediaDrawableOutSelected; + public static Drawable checkDrawable; + public static Drawable halfCheckDrawable; + public static Drawable clockDrawable; + public static Drawable broadcastDrawable; + public static Drawable checkMediaDrawable; + public static Drawable halfCheckMediaDrawable; + public static Drawable clockMediaDrawable; + public static Drawable broadcastMediaDrawable; + public static Drawable errorDrawable; + public static Drawable backgroundBlack; + public static Drawable backgroundBlue; + public static Drawable mediaBackgroundDrawable; + + public static Drawable geoInDrawable; + public static Drawable geoOutDrawable; + + public static Drawable[][] audioStatesDrawable = new Drawable[10][2]; + + public static Drawable placeholderDocInDrawable; + public static Drawable placeholderDocOutDrawable; + public static Drawable videoIconDrawable; + public static Drawable docMenuInDrawable; + public static Drawable docMenuOutDrawable; + public static Drawable[] buttonStatesDrawables = new Drawable[8]; + public static Drawable[][] buttonStatesDrawablesDoc = new Drawable[3][2]; + + public static void loadRecources(Context context) { + if (backgroundDrawableIn == null) { + backgroundDrawableIn = context.getResources().getDrawable(R.drawable.msg_in); + backgroundDrawableInSelected = context.getResources().getDrawable(R.drawable.msg_in_selected); + backgroundDrawableOut = context.getResources().getDrawable(R.drawable.msg_out); + backgroundDrawableOutSelected = context.getResources().getDrawable(R.drawable.msg_out_selected); + backgroundMediaDrawableIn = context.getResources().getDrawable(R.drawable.msg_in_photo); + backgroundMediaDrawableInSelected = context.getResources().getDrawable(R.drawable.msg_in_photo_selected); + backgroundMediaDrawableOut = context.getResources().getDrawable(R.drawable.msg_out_photo); + backgroundMediaDrawableOutSelected = context.getResources().getDrawable(R.drawable.msg_out_photo_selected); + checkDrawable = context.getResources().getDrawable(R.drawable.msg_check); + halfCheckDrawable = context.getResources().getDrawable(R.drawable.msg_halfcheck); + clockDrawable = context.getResources().getDrawable(R.drawable.msg_clock); + checkMediaDrawable = context.getResources().getDrawable(R.drawable.msg_check_w); + halfCheckMediaDrawable = context.getResources().getDrawable(R.drawable.msg_halfcheck_w); + clockMediaDrawable = context.getResources().getDrawable(R.drawable.msg_clock_photo); + errorDrawable = context.getResources().getDrawable(R.drawable.msg_warning); + mediaBackgroundDrawable = context.getResources().getDrawable(R.drawable.phototime); + broadcastDrawable = context.getResources().getDrawable(R.drawable.broadcast3); + broadcastMediaDrawable = context.getResources().getDrawable(R.drawable.broadcast4); + backgroundBlack = context.getResources().getDrawable(R.drawable.system_black); + backgroundBlue = context.getResources().getDrawable(R.drawable.system_blue); + + audioStatesDrawable[0][0] = context.getResources().getDrawable(R.drawable.play_w2); + audioStatesDrawable[0][1] = context.getResources().getDrawable(R.drawable.play_w2_pressed); + audioStatesDrawable[1][0] = context.getResources().getDrawable(R.drawable.pause_w2); + audioStatesDrawable[1][1] = context.getResources().getDrawable(R.drawable.pause_w2_pressed); + audioStatesDrawable[2][0] = context.getResources().getDrawable(R.drawable.download_g); + audioStatesDrawable[2][1] = context.getResources().getDrawable(R.drawable.download_g_pressed); + audioStatesDrawable[3][0] = context.getResources().getDrawable(R.drawable.pause_g); + audioStatesDrawable[3][1] = context.getResources().getDrawable(R.drawable.pause_g_pressed); + audioStatesDrawable[4][0] = context.getResources().getDrawable(R.drawable.cancel_g); + audioStatesDrawable[4][1] = context.getResources().getDrawable(R.drawable.cancel_g_pressed); + audioStatesDrawable[5][0] = context.getResources().getDrawable(R.drawable.play_w); + audioStatesDrawable[5][1] = context.getResources().getDrawable(R.drawable.play_w_pressed); + audioStatesDrawable[6][0] = context.getResources().getDrawable(R.drawable.pause_w); + audioStatesDrawable[6][1] = context.getResources().getDrawable(R.drawable.pause_w_pressed); + audioStatesDrawable[7][0] = context.getResources().getDrawable(R.drawable.download_b); + audioStatesDrawable[7][1] = context.getResources().getDrawable(R.drawable.download_b_pressed); + audioStatesDrawable[8][0] = context.getResources().getDrawable(R.drawable.pause_b); + audioStatesDrawable[8][1] = context.getResources().getDrawable(R.drawable.pause_b_pressed); + audioStatesDrawable[9][0] = context.getResources().getDrawable(R.drawable.cancel_b); + audioStatesDrawable[9][1] = context.getResources().getDrawable(R.drawable.cancel_b_pressed); + + placeholderDocInDrawable = context.getResources().getDrawable(R.drawable.doc_blue); + placeholderDocOutDrawable = context.getResources().getDrawable(R.drawable.doc_green); + buttonStatesDrawables[0] = context.getResources().getDrawable(R.drawable.photoload); + buttonStatesDrawables[1] = context.getResources().getDrawable(R.drawable.photocancel); + buttonStatesDrawables[2] = context.getResources().getDrawable(R.drawable.photogif); + buttonStatesDrawables[3] = context.getResources().getDrawable(R.drawable.playvideo); + buttonStatesDrawables[4] = context.getResources().getDrawable(R.drawable.photopause); + buttonStatesDrawables[5] = context.getResources().getDrawable(R.drawable.burn); + buttonStatesDrawables[6] = context.getResources().getDrawable(R.drawable.circle); + buttonStatesDrawables[7] = context.getResources().getDrawable(R.drawable.photocheck); + buttonStatesDrawablesDoc[0][0] = context.getResources().getDrawable(R.drawable.docload_b); + buttonStatesDrawablesDoc[1][0] = context.getResources().getDrawable(R.drawable.doccancel_b); + buttonStatesDrawablesDoc[2][0] = context.getResources().getDrawable(R.drawable.docpause_b); + buttonStatesDrawablesDoc[0][1] = context.getResources().getDrawable(R.drawable.docload_g); + buttonStatesDrawablesDoc[1][1] = context.getResources().getDrawable(R.drawable.doccancel_g); + buttonStatesDrawablesDoc[2][1] = context.getResources().getDrawable(R.drawable.docpause_g); + videoIconDrawable = context.getResources().getDrawable(R.drawable.ic_video); + docMenuInDrawable = context.getResources().getDrawable(R.drawable.doc_actions_b); + docMenuOutDrawable = context.getResources().getDrawable(R.drawable.doc_actions_g); + + geoInDrawable = context.getResources().getDrawable(R.drawable.location_b); + geoOutDrawable = context.getResources().getDrawable(R.drawable.location_g); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SendingFileDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SendingFileDrawable.java new file mode 100644 index 000000000..103a440af --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SendingFileDrawable.java @@ -0,0 +1,126 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Components; + +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.view.animation.DecelerateInterpolator; + +import org.telegram.android.AndroidUtilities; + +public class SendingFileDrawable extends Drawable { + + private float radOffset = 0; + private float currentProgress = 0; + private float animationProgressStart = 0; + private long currentProgressTime = 0; + private float animatedProgressValue = 0; + private RectF cicleRect = new RectF(); + private boolean isChat = false; + private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + private long lastUpdateTime = 0; + private boolean started = false; + private static DecelerateInterpolator decelerateInterpolator = null; + + public SendingFileDrawable() { + super(); + paint.setColor(0xffd7e8f7); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(AndroidUtilities.dp(2)); + paint.setStrokeCap(Paint.Cap.ROUND); + decelerateInterpolator = new DecelerateInterpolator(); + } + + public void setIsChat(boolean value) { + isChat = value; + } + + public void setProgress(float value, boolean animated) { + if (!animated) { + animatedProgressValue = value; + animationProgressStart = value; + } else { + animationProgressStart = animatedProgressValue; + } + currentProgress = value; + currentProgressTime = 0; + + invalidateSelf(); + } + + private void update() { + long newTime = System.currentTimeMillis(); + long dt = newTime - lastUpdateTime; + lastUpdateTime = newTime; + + if (animatedProgressValue != 1) { + radOffset += 360 * dt / 1000.0f; + float progressDiff = currentProgress - animationProgressStart; + if (progressDiff > 0) { + currentProgressTime += dt; + if (currentProgressTime >= 300) { + animatedProgressValue = currentProgress; + animationProgressStart = currentProgress; + currentProgressTime = 0; + } else { + animatedProgressValue = animationProgressStart + progressDiff * decelerateInterpolator.getInterpolation(currentProgressTime / 300.0f); + } + } + invalidateSelf(); + } + } + + public void start() { + lastUpdateTime = System.currentTimeMillis(); + started = true; + invalidateSelf(); + } + + public void stop() { + started = false; + } + + @Override + public void draw(Canvas canvas) { + cicleRect.set(AndroidUtilities.dp(1), AndroidUtilities.dp(isChat ? 3 : 4), AndroidUtilities.dp(10), AndroidUtilities.dp(isChat ? 11 : 12)); + canvas.drawArc(cicleRect, -90 + radOffset, Math.max(60, 360 * animatedProgressValue), false, paint); + + if (started) { + update(); + } + } + + @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(14); + } + + @Override + public int getIntrinsicHeight() { + return AndroidUtilities.dp(14); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SendingFileEx2Drawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SendingFileEx2Drawable.java new file mode 100644 index 000000000..48aae49e3 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SendingFileEx2Drawable.java @@ -0,0 +1,97 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Components; + +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; + +import org.telegram.android.AndroidUtilities; + +public class SendingFileEx2Drawable extends Drawable { + + private boolean isChat = false; + private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + private long lastUpdateTime = 0; + private boolean started = false; + private float progress; + + public SendingFileEx2Drawable() { + super(); + paint.setColor(0xffd7e8f7); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(AndroidUtilities.dp(3)); + paint.setStrokeCap(Paint.Cap.ROUND); + } + + public void setIsChat(boolean value) { + isChat = value; + } + + private void update() { + long newTime = System.currentTimeMillis(); + long dt = newTime - lastUpdateTime; + lastUpdateTime = newTime; + if (dt > 50) { + dt = 50; + } + progress += dt / 1000.0f; + while (progress > 1.0f) { + progress -= 1.0f; + } + invalidateSelf(); + } + + public void start() { + lastUpdateTime = System.currentTimeMillis(); + started = true; + invalidateSelf(); + } + + public void stop() { + started = false; + } + + @Override + public void draw(Canvas canvas) { + int start = (int) (progress <= 0.5f ? AndroidUtilities.dp(1) : AndroidUtilities.dp(11) * (progress - 0.5f) * 2); + int end = (int) (progress >= 0.5f ? AndroidUtilities.dp(11) : AndroidUtilities.dp(11) * progress * 2); + canvas.drawLine(start, AndroidUtilities.dp(isChat ? 11 : 12), end, AndroidUtilities.dp(isChat ? 11 : 12), paint); + + if (started) { + update(); + } + } + + @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(18); + } + + @Override + public int getIntrinsicHeight() { + return AndroidUtilities.dp(14); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SendingFileExDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SendingFileExDrawable.java new file mode 100644 index 000000000..9b5d5069b --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SendingFileExDrawable.java @@ -0,0 +1,107 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Components; + +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; + +import org.telegram.android.AndroidUtilities; + +public class SendingFileExDrawable extends Drawable { + + private boolean isChat = false; + private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + private long lastUpdateTime = 0; + private boolean started = false; + private float progress; + + public SendingFileExDrawable() { + super(); + paint.setColor(0xffd7e8f7); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(AndroidUtilities.dp(2)); + paint.setStrokeCap(Paint.Cap.ROUND); + } + + public void setIsChat(boolean value) { + isChat = value; + } + + private void update() { + long newTime = System.currentTimeMillis(); + long dt = newTime - lastUpdateTime; + lastUpdateTime = newTime; + if (dt > 50) { + dt = 50; + } + progress += dt / 500.0f; + while (progress > 1.0f) { + progress -= 1.0f; + } + invalidateSelf(); + } + + public void start() { + lastUpdateTime = System.currentTimeMillis(); + started = true; + invalidateSelf(); + } + + public void stop() { + started = false; + } + + @Override + public void draw(Canvas canvas) { + + for (int a = 0; a < 3; a++) { + if (a == 0) { + paint.setAlpha((int) (255 * progress)); + } else if (a == 2) { + paint.setAlpha((int) (255 * (1.0f - progress))); + } else { + paint.setAlpha(255); + } + float side = AndroidUtilities.dp(5) * a + AndroidUtilities.dp(5) * progress; + canvas.drawLine(side, AndroidUtilities.dp(isChat ? 3 : 4), side + AndroidUtilities.dp(4), AndroidUtilities.dp(isChat ? 7 : 8), paint); + canvas.drawLine(side, AndroidUtilities.dp(isChat ? 11 : 12), side + AndroidUtilities.dp(4), AndroidUtilities.dp(isChat ? 7 : 8), paint); + } + + if (started) { + update(); + } + } + + @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(18); + } + + @Override + public int getIntrinsicHeight() { + return AndroidUtilities.dp(14); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SimpleTextView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SimpleTextView.java new file mode 100644 index 000000000..2bbe59040 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SimpleTextView.java @@ -0,0 +1,126 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Components; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Typeface; +import android.text.Layout; +import android.text.SpannableStringBuilder; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.text.TextUtils; +import android.view.Gravity; +import android.view.View; + +import org.telegram.android.AndroidUtilities; + +public class SimpleTextView extends View { + + private Layout layout; + private TextPaint textPaint; + private int gravity; + private CharSequence text; + private SpannableStringBuilder spannableStringBuilder; + + private int offsetX; + private boolean wasLayout = false; + + public SimpleTextView(Context context) { + super(context); + textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + } + + public void setTextColor(int color) { + textPaint.setColor(color); + } + + public void setTextSize(int size) { + textPaint.setTextSize(AndroidUtilities.dp(size)); + } + + public void setGravity(int value) { + gravity = value; + } + + public void setTypeface(Typeface typeface) { + textPaint.setTypeface(typeface); + } + + private void createLayout(int width) { + if (text != null) { + try { + CharSequence string = TextUtils.ellipsize(text, textPaint, width, TextUtils.TruncateAt.END); + layout = new StaticLayout(string, 0, string.length(), textPaint, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + + /*if (metrics == null) { + metrics = BoringLayout.isBoring(text, textPaint); + } + if (layout == null) { + layout = BoringLayout.make(text, textPaint, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, metrics, false, TextUtils.TruncateAt.END, width); + } else { + layout = ((BoringLayout) layout).replaceOrMake(text, textPaint, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, metrics, false, TextUtils.TruncateAt.END, width); + }*/ + + /*if (spannableStringBuilder == null) { + spannableStringBuilder = new SpannableStringBuilder(text); + layout = new DynamicLayout(text, text, textPaint, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false, TextUtils.TruncateAt.END, width); + } else { + spannableStringBuilder.replace(0, text.length(), text); + }*/ + + if (layout.getLineCount() > 0) { + if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.LEFT) { + offsetX = -(int) layout.getLineLeft(0); + } else if (layout.getLineLeft(0) == 0) { + offsetX = (int) (width - layout.getLineWidth(0)); + } else { + offsetX = 0; + } + } + } catch (Exception e) { + //ignore + } + } + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + if (changed) { + createLayout(right - left); + invalidate(); + wasLayout = true; + } + } + + public void setText(CharSequence value) { + text = value; + if (wasLayout) { + createLayout(getMeasuredWidth()); + invalidate(); + } else { + requestLayout(); + } + } + + @Override + protected void onDraw(Canvas canvas) { + if (layout != null) { + if (offsetX != 0) { + canvas.save(); + canvas.translate(offsetX, 0); + } + layout.draw(canvas); + if (offsetX != 0) { + canvas.restore(); + } + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierFrameLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierFrameLayout.java new file mode 100644 index 000000000..864e9e1ea --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierFrameLayout.java @@ -0,0 +1,111 @@ +/* + * This is the source code of Telegram for Android v. 2.x.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2015. + */ + +package org.telegram.ui.Components; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.view.View; +import android.widget.FrameLayout; + +import org.telegram.android.AndroidUtilities; +import org.telegram.messenger.FileLog; + +public class SizeNotifierFrameLayout extends FrameLayout { + + private Rect rect = new Rect(); + private Drawable backgroundDrawable; + private int keyboardHeight; + private SizeNotifierRelativeLayout.SizeNotifierRelativeLayoutDelegate delegate; + + public SizeNotifierFrameLayout(Context context) { + super(context); + setWillNotDraw(false); + } + + public SizeNotifierFrameLayout(Context context, android.util.AttributeSet attrs) { + super(context, attrs); + setWillNotDraw(false); + } + + public SizeNotifierFrameLayout(Context context, android.util.AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setWillNotDraw(false); + } + + public void setBackgroundImage(int resourceId) { + try { + backgroundDrawable = getResources().getDrawable(resourceId); + } catch (Throwable e) { + FileLog.e("tmessages", e); + } + } + + public void setBackgroundImage(Drawable bitmap) { + backgroundDrawable = bitmap; + } + + public Drawable getBackgroundImage() { + return backgroundDrawable; + } + + public void setDelegate(SizeNotifierRelativeLayout.SizeNotifierRelativeLayoutDelegate delegate) { + this.delegate = delegate; + } + + @SuppressLint("DrawAllocation") + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + notifyHeightChanged(); + } + + public void notifyHeightChanged() { + if (delegate != null) { + View rootView = this.getRootView(); + int usableViewHeight = rootView.getHeight() - AndroidUtilities.statusBarHeight - AndroidUtilities.getViewInset(rootView); + this.getWindowVisibleDisplayFrame(rect); + keyboardHeight = usableViewHeight - (rect.bottom - rect.top); + final boolean isWidthGreater = AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y; + post(new Runnable() { + @Override + public void run() { + if (delegate != null) { + delegate.onSizeChanged(keyboardHeight, isWidthGreater); + } + } + }); + } + } + + @Override + protected void onDraw(Canvas canvas) { + if (backgroundDrawable != null) { + if (backgroundDrawable instanceof ColorDrawable) { + backgroundDrawable.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight()); + backgroundDrawable.draw(canvas); + } else { + float scaleX = (float) getMeasuredWidth() / (float) backgroundDrawable.getIntrinsicWidth(); + float scaleY = (float) (getMeasuredHeight() + keyboardHeight) / (float) backgroundDrawable.getIntrinsicHeight(); + float scale = scaleX < scaleY ? scaleY : scaleX; + int width = (int) Math.ceil(backgroundDrawable.getIntrinsicWidth() * scale); + int height = (int) Math.ceil(backgroundDrawable.getIntrinsicHeight() * scale); + int x = (getMeasuredWidth() - width) / 2; + int y = (getMeasuredHeight() - height + keyboardHeight) / 2; + backgroundDrawable.setBounds(x, y, x + width, y + height); + backgroundDrawable.draw(canvas); + } + } else { + super.onDraw(canvas); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierRelativeLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierRelativeLayout.java index 54ec83b9a..8802e8505 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierRelativeLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierRelativeLayout.java @@ -77,7 +77,6 @@ public class SizeNotifierRelativeLayout extends RelativeLayout { this.getWindowVisibleDisplayFrame(rect); keyboardHeight = usableViewHeight - (rect.bottom - rect.top); final boolean isWidthGreater = AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y; - FileLog.e("tmessages", "isWidthGreater = " + isWidthGreater + " height = " + keyboardHeight); post(new Runnable() { @Override public void run() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierRelativeLayoutPhoto.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierRelativeLayoutPhoto.java new file mode 100644 index 000000000..ef2d6055e --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierRelativeLayoutPhoto.java @@ -0,0 +1,58 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui.Components; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Rect; +import android.view.View; +import android.widget.RelativeLayout; + +import org.telegram.android.AndroidUtilities; + +public class SizeNotifierRelativeLayoutPhoto extends RelativeLayout { + + public interface SizeNotifierRelativeLayoutPhotoDelegate { + void onSizeChanged(int keyboardHeight, boolean isWidthGreater); + } + + private Rect rect = new Rect(); + private int keyboardHeight; + private SizeNotifierRelativeLayoutPhotoDelegate delegate; + + public SizeNotifierRelativeLayoutPhoto(Context context) { + super(context); + } + + public void setDelegate(SizeNotifierRelativeLayoutPhotoDelegate delegate) { + this.delegate = delegate; + } + + @SuppressLint("DrawAllocation") + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + + if (delegate != null) { + View rootView = this.getRootView(); + int usableViewHeight = rootView.getHeight() - AndroidUtilities.getViewInset(rootView); + this.getWindowVisibleDisplayFrame(rect); + keyboardHeight = (rect.bottom - rect.top) - usableViewHeight; + final boolean isWidthGreater = AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y; + post(new Runnable() { + @Override + public void run() { + if (delegate != null) { + delegate.onSizeChanged(keyboardHeight, isWidthGreater); + } + } + }); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SlidingTabView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SlidingTabView.java index 4c01ff275..1ba3e974c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SlidingTabView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SlidingTabView.java @@ -12,7 +12,6 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Typeface; -import android.util.AttributeSet; import android.util.TypedValue; import android.view.Gravity; import android.view.View; @@ -41,33 +40,15 @@ public class SlidingTabView extends LinearLayout { private float startAnimationX = 0; private DecelerateInterpolator interpolator; - private void init() { + public SlidingTabView(Context context) { + super(context); setOrientation(HORIZONTAL); setWeightSum(100); paint.setColor(0xffffffff); + setWillNotDraw(false); interpolator = new DecelerateInterpolator(); } - public SlidingTabView(Context context) { - super(context); - init(); - } - - public SlidingTabView(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - public SlidingTabView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(); - } - - public SlidingTabView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - init(); - } - public void addTextTab(final int position, String title) { TextView tab = new TextView(getContext()); tab.setText(title); @@ -87,7 +68,7 @@ public class SlidingTabView extends LinearLayout { }); addView(tab); LayoutParams layoutParams = (LayoutParams)tab.getLayoutParams(); - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.width = 0; layoutParams.weight = 50; tab.setLayoutParams(layoutParams); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/StaticLayoutEx.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/StaticLayoutEx.java index 4700a15e6..f918c1b3c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/StaticLayoutEx.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/StaticLayoutEx.java @@ -10,6 +10,7 @@ package org.telegram.ui.Components; import android.os.Build; import android.text.Layout; +import android.text.SpannableStringBuilder; import android.text.StaticLayout; import android.text.TextDirectionHeuristic; import android.text.TextDirectionHeuristics; @@ -78,7 +79,7 @@ public class StaticLayoutEx { } public static StaticLayout createStaticLayout(CharSequence source, int bufstart, int bufend, TextPaint paint, int outerWidth, Layout.Alignment align, float spacingMult, float spacingAdd, boolean includePad, TextUtils.TruncateAt ellipsize, int ellipsisWidth, int maxLines) { - if (Build.VERSION.SDK_INT >= 14) { + /*if (Build.VERSION.SDK_INT >= 14) { init(); try { sConstructorArgs[0] = source; @@ -98,17 +99,26 @@ public class StaticLayoutEx { } catch (Exception e) { FileLog.e("tmessages", e); } - } + }*/ try { if (maxLines == 1) { - return new StaticLayout(source, bufstart, bufend, paint, outerWidth, align, spacingMult, spacingAdd, includePad, ellipsize, ellipsisWidth); + CharSequence text = TextUtils.ellipsize(source, paint, ellipsisWidth, TextUtils.TruncateAt.END); + return new StaticLayout(text, 0, text.length(), paint, outerWidth, align, spacingMult, spacingAdd, includePad); } else { StaticLayout layout = new StaticLayout(source, paint, outerWidth, align, spacingMult, spacingAdd, includePad); if (layout.getLineCount() <= maxLines) { return layout; } else { - int off = layout.getOffsetForHorizontal(maxLines - 1, layout.getLineWidth(maxLines - 1)); - return new StaticLayout(source.subSequence(0, off), paint, outerWidth, align, spacingMult, spacingAdd, includePad); + int off; + float left = layout.getLineLeft(maxLines - 1); + if (left != 0) { + off = layout.getOffsetForHorizontal(maxLines - 1, left); + } else { + off = layout.getOffsetForHorizontal(maxLines - 1, layout.getLineWidth(maxLines - 1)); + } + SpannableStringBuilder stringBuilder = new SpannableStringBuilder(source.subSequence(0, Math.max(0, off - 1))); + stringBuilder.append("\u2026"); + return new StaticLayout(stringBuilder, paint, outerWidth, align, spacingMult, spacingAdd, includePad); } } } catch (Exception e) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/StickersAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickersAlert.java new file mode 100644 index 000000000..49ace5b72 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickersAlert.java @@ -0,0 +1,138 @@ +/* + * This is the source code of Telegram for Android v. 2.x.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2015. + */ + +package org.telegram.ui.Components; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.database.DataSetObserver; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.FrameLayout; +import android.widget.GridView; + +import org.telegram.android.AndroidUtilities; +import org.telegram.android.LocaleController; +import org.telegram.android.NotificationCenter; +import org.telegram.messenger.R; +import org.telegram.messenger.TLRPC; +import org.telegram.ui.Cells.StickerEmojiCell; + +import java.util.ArrayList; + +public class StickersAlert extends AlertDialog implements NotificationCenter.NotificationCenterDelegate { + + private ArrayList stickers; + private GridView gridView; + + public StickersAlert(Context context, TLRPC.TL_stickerSet set, ArrayList arrayList) { + super(context); + stickers = arrayList; + + FrameLayout container = new FrameLayout(context) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec((int) Math.min(Math.ceil(stickers.size() / 4.0f) * AndroidUtilities.dp(82), AndroidUtilities.displaySize.y / 5 * 3), MeasureSpec.EXACTLY)); + } + }; + setView(container, AndroidUtilities.dp(16), 0, AndroidUtilities.dp(16), 0); + + gridView = new GridView(context); + gridView.setNumColumns(4); + gridView.setAdapter(new GridAdapter(context)); + gridView.setVerticalScrollBarEnabled(false); + container.addView(gridView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + + if (set.id == -1) { + setTitle(LocaleController.getString("GeniusStickerPackName", R.string.GeniusStickerPackName)); + } else { + setTitle(set.title); + } + + NotificationCenter.getInstance().addObserver(this, NotificationCenter.emojiDidLoaded); + + setOnShowListener(new OnShowListener() { + @Override + public void onShow(DialogInterface arg0) { + if (getButton(AlertDialog.BUTTON_NEUTRAL) != null) { + getButton(AlertDialog.BUTTON_NEUTRAL).setTextColor(0xffcd5a5a); + } + if (getButton(AlertDialog.BUTTON_POSITIVE) != null) { + getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(0xff37a919); + } + } + }); + } + + @Override + public void dismiss() { + super.dismiss(); + NotificationCenter.getInstance().removeObserver(this, NotificationCenter.emojiDidLoaded); + } + + @Override + public void didReceivedNotification(int id, Object... args) { + if (id == NotificationCenter.emojiDidLoaded) { + if (gridView != null) { + gridView.invalidateViews(); + } + } + } + + private class GridAdapter extends BaseAdapter { + + Context context; + + public GridAdapter(Context context) { + this.context = context; + } + + public int getCount() { + return stickers.size(); + } + + public Object getItem(int i) { + return stickers.get(i); + } + + public long getItemId(int i) { + return stickers.get(i).id; + } + + @Override + public boolean areAllItemsEnabled() { + return false; + } + + @Override + public boolean isEnabled(int position) { + return false; + } + + public View getView(int i, View view, ViewGroup viewGroup) { + if (view == null) { + view = new StickerEmojiCell(context) { + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(82), MeasureSpec.EXACTLY)); + } + }; + } + ((StickerEmojiCell) view).setSticker(stickers.get(i), true); + return view; + } + + @Override + public void unregisterDataSetObserver(DataSetObserver observer) { + if (observer != null) { + super.unregisterDataSetObserver(observer); + } + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Switch.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Switch.java index f1a940f40..fa6740a9d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Switch.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Switch.java @@ -33,7 +33,7 @@ import android.widget.CompoundButton; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; import org.telegram.messenger.R; -import org.telegram.ui.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; public class Switch extends CompoundButton { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/TypingDotsDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/TypingDotsDrawable.java index 393482f89..d644d7d6d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/TypingDotsDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/TypingDotsDrawable.java @@ -17,6 +17,7 @@ import android.view.animation.DecelerateInterpolator; import org.telegram.android.AndroidUtilities; public class TypingDotsDrawable extends Drawable { + private boolean isChat = false; private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); private float[] scales = new float[3]; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/URLSpanNoUnderlineBold.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/URLSpanNoUnderlineBold.java new file mode 100644 index 000000000..42f98f0e8 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/URLSpanNoUnderlineBold.java @@ -0,0 +1,26 @@ +/* + * This is the source code of Telegram for Android v. 2.x.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2015. + */ + +package org.telegram.ui.Components; + +import android.text.TextPaint; + +import org.telegram.android.AndroidUtilities; + +public class URLSpanNoUnderlineBold extends URLSpanNoUnderline { + public URLSpanNoUnderlineBold(String url) { + super(url); + } + + @Override + public void updateDrawState(TextPaint ds) { + super.updateDrawState(ds); + ds.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + ds.setUnderlineText(false); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/WebFrameLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/WebFrameLayout.java new file mode 100644 index 000000000..7d597cce6 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/WebFrameLayout.java @@ -0,0 +1,232 @@ +/* + * This is the source code of Telegram for Android v. 2.x.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2015. + */ + +package org.telegram.ui.Components; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.provider.Browser; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.webkit.CookieManager; +import android.webkit.WebChromeClient; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; + +import org.telegram.android.AndroidUtilities; +import org.telegram.android.LocaleController; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.R; +import org.telegram.ui.ActionBar.BottomSheet; + +import java.util.HashMap; + +public class WebFrameLayout extends FrameLayout { + + private WebView webView; + private BottomSheet dialog; + private View customView; + private FrameLayout fullscreenVideoContainer; + private WebChromeClient.CustomViewCallback customViewCallback; + private ProgressBar progressBar; + + private int width; + private int height; + private String openUrl; + + @SuppressLint("SetJavaScriptEnabled") + public WebFrameLayout(Context context, BottomSheet parentDialog, String title, String originalUrl, final String url, int w, int h) { + super(context); + + openUrl = originalUrl; + width = w; + height = h; + dialog = parentDialog; + + fullscreenVideoContainer = new FrameLayout(context); + fullscreenVideoContainer.setBackgroundColor(0xff000000); + dialog.getContainer().addView(fullscreenVideoContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + fullscreenVideoContainer.setVisibility(INVISIBLE); + + LinearLayout linearLayout = new LinearLayout(context); + linearLayout.setOrientation(LinearLayout.HORIZONTAL); + addView(linearLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 32, Gravity.LEFT | Gravity.TOP)); + + TextView textView = new TextView(context); + textView.setTextColor(0xff666666); + textView.setText(title); + textView.setSingleLine(true); + textView.setEllipsize(TextUtils.TruncateAt.END); + textView.setGravity(Gravity.CENTER_VERTICAL); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); + linearLayout.addView(textView, LayoutHelper.createLinear(0, LayoutHelper.MATCH_PARENT, 1.0f, 16, 0, 0, 0)); + + textView = new TextView(context); + textView.setTextColor(0xff999999); + textView.setText(LocaleController.getString("OpenInBrowser", R.string.OpenInBrowser)); + textView.setGravity(Gravity.CENTER_VERTICAL); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12); + linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, 16, 0, 0, 0)); + textView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + Uri uri = Uri.parse(openUrl); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + intent.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName()); + getContext().startActivity(intent); + if (dialog != null) { + dialog.dismiss(); + } + } + }); + + textView = new TextView(context); + textView.setTextColor(0xff999999); + textView.setText(LocaleController.getString("CopyUrl", R.string.CopyUrl)); + textView.setGravity(Gravity.CENTER_VERTICAL); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12); + linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, 16, 0, 16, 0)); + textView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (Build.VERSION.SDK_INT < 11) { + android.text.ClipboardManager clipboard = (android.text.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE); + clipboard.setText(openUrl); + } else { + android.content.ClipboardManager clipboard = (android.content.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE); + android.content.ClipData clip = android.content.ClipData.newPlainText("label", openUrl); + clipboard.setPrimaryClip(clip); + } + Toast.makeText(getContext(), LocaleController.getString("LinkCopied", R.string.LinkCopied), Toast.LENGTH_SHORT).show(); + if (dialog != null) { + dialog.dismiss(); + } + } + }); + + View lineView = new View(context); + lineView.setBackgroundColor(0xffcdcdcd); + addView(lineView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 1, Gravity.TOP | Gravity.LEFT, 0, 40, 0, 0)); + + webView = new WebView(context); + webView.getSettings().setJavaScriptEnabled(true); + String userAgent = webView.getSettings().getUserAgentString(); + if (userAgent != null) { + userAgent = userAgent.replace("Android", ""); + webView.getSettings().setUserAgentString(userAgent); + } + if (Build.VERSION.SDK_INT >= 21) { + webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); + CookieManager cookieManager = CookieManager.getInstance(); + cookieManager.setAcceptThirdPartyCookies(webView, true); + } + + webView.setWebChromeClient(new WebChromeClient() { + + @Override + public void onShowCustomView(View view, int requestedOrientation, CustomViewCallback callback) { + onShowCustomView(view, callback); + } + + @Override + public void onShowCustomView(View view, CustomViewCallback callback) { + if (customView != null) { + callback.onCustomViewHidden(); + return; + } + customView = view; + if (dialog != null) { + dialog.getSheetContainer().setVisibility(INVISIBLE); + fullscreenVideoContainer.setVisibility(VISIBLE); + fullscreenVideoContainer.addView(view, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + } + customViewCallback = callback; + } + + @Override + public void onHideCustomView() { + super.onHideCustomView(); + if (customView == null) { + return; + } + if (dialog != null) { + dialog.getSheetContainer().setVisibility(VISIBLE); + fullscreenVideoContainer.setVisibility(INVISIBLE); + fullscreenVideoContainer.removeView(customView); + } + if (customViewCallback != null && !customViewCallback.getClass().getName().contains(".chromium.")) { + customViewCallback.onCustomViewHidden(); + } + customView = null; + } + }); + + webView.setWebViewClient(new WebViewClient() { + @Override + public void onLoadResource(WebView view, String url) { + super.onLoadResource(view, url); + } + + + @Override + public void onPageFinished(WebView view, String url) { + super.onPageFinished(view, url); + progressBar.setVisibility(INVISIBLE); + } + }); + + addView(webView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT, 8, 49, 8, 0)); + + progressBar = new ProgressBar(context); + addView(progressBar, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 8, 24, 8, 0)); + + setOnTouchListener(new OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + return true; + } + }); + + parentDialog.setDelegate(new BottomSheet.BottomSheetDelegate() { + @Override + public void onOpenAnimationEnd() { + HashMap args = new HashMap<>(); + args.put("Referer", "http://youtube.com"); + webView.loadUrl(url, args); + } + }); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + webView.stopLoading(); + webView.loadUrl("about:blank"); + webView.destroy(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int parentWidth = MeasureSpec.getSize(widthMeasureSpec); + float scale = width / parentWidth; + int h = (int) Math.min(height / scale, AndroidUtilities.displaySize.y / 2); + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(h + AndroidUtilities.dp(49), MeasureSpec.EXACTLY)); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ContactAddActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ContactAddActivity.java index cf56dd75b..c09173f9d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ContactAddActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ContactAddActivity.java @@ -41,6 +41,7 @@ import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; public class ContactAddActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { @@ -129,8 +130,8 @@ public class ContactAddActivity extends BaseFragment implements NotificationCent layoutParams.topMargin = AndroidUtilities.dp(24); layoutParams.leftMargin = AndroidUtilities.dp(24); layoutParams.rightMargin = AndroidUtilities.dp(24); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; frameLayout.setLayoutParams(layoutParams); avatarImage = new BackupImageView(context); @@ -153,8 +154,8 @@ public class ContactAddActivity extends BaseFragment implements NotificationCent nameTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); frameLayout.addView(nameTextView); layoutParams3 = (FrameLayout.LayoutParams) nameTextView.getLayoutParams(); - layoutParams3.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams3.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams3.width = LayoutHelper.WRAP_CONTENT; + layoutParams3.height = LayoutHelper.WRAP_CONTENT; layoutParams3.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 0 : 80); layoutParams3.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 80 : 0); layoutParams3.topMargin = AndroidUtilities.dp(3); @@ -171,8 +172,8 @@ public class ContactAddActivity extends BaseFragment implements NotificationCent onlineTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT)); frameLayout.addView(onlineTextView); layoutParams3 = (FrameLayout.LayoutParams) onlineTextView.getLayoutParams(); - layoutParams3.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams3.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams3.width = LayoutHelper.WRAP_CONTENT; + layoutParams3.height = LayoutHelper.WRAP_CONTENT; layoutParams3.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 0 : 80); layoutParams3.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 80 : 0); layoutParams3.topMargin = AndroidUtilities.dp(32); @@ -197,7 +198,7 @@ public class ContactAddActivity extends BaseFragment implements NotificationCent layoutParams.height = AndroidUtilities.dp(36); layoutParams.leftMargin = AndroidUtilities.dp(24); layoutParams.rightMargin = AndroidUtilities.dp(24); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; firstNameField.setLayoutParams(layoutParams); firstNameField.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override @@ -229,7 +230,7 @@ public class ContactAddActivity extends BaseFragment implements NotificationCent layoutParams.height = AndroidUtilities.dp(36); layoutParams.leftMargin = AndroidUtilities.dp(24); layoutParams.rightMargin = AndroidUtilities.dp(24); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; lastNameField.setLayoutParams(layoutParams); lastNameField.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java index 39b4310bc..99a4b4475 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java @@ -49,6 +49,7 @@ import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.LetterSectionsListView; import java.util.ArrayList; @@ -69,6 +70,7 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter private boolean returnAsResult; private boolean createSecretChat; private boolean creatingChat = false; + private int chat_id; private String selectAlertString = null; private HashMap ignoreUsers; private boolean allowUsernameSearch = true; @@ -97,6 +99,7 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter createSecretChat = arguments.getBoolean("createSecretChat", false); selectAlertString = arguments.getString("selectAlertString"); allowUsernameSearch = arguments.getBoolean("allowUsernameSearch", true); + chat_id = arguments.getInt("chat_id", 0); } else { needPhonebook = true; } @@ -197,7 +200,7 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter item.getSearchField().setHint(LocaleController.getString("Search", R.string.Search)); searchListViewAdapter = new SearchAdapter(context, ignoreUsers, allowUsernameSearch); - listViewAdapter = new ContactsAdapter(context, onlyUsers, needPhonebook, ignoreUsers); + listViewAdapter = new ContactsAdapter(context, onlyUsers, needPhonebook, ignoreUsers, chat_id != 0); fragmentView = new FrameLayout(context); @@ -206,8 +209,8 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter emptyTextLayout.setOrientation(LinearLayout.VERTICAL); ((FrameLayout) fragmentView).addView(emptyTextLayout); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) emptyTextLayout.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP; emptyTextLayout.setLayoutParams(layoutParams); emptyTextLayout.setOnTouchListener(new View.OnTouchListener() { @@ -224,16 +227,16 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter emptyTextView.setText(LocaleController.getString("NoContacts", R.string.NoContacts)); emptyTextLayout.addView(emptyTextView); LinearLayout.LayoutParams layoutParams1 = (LinearLayout.LayoutParams) emptyTextView.getLayoutParams(); - layoutParams1.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.MATCH_PARENT; layoutParams1.weight = 0.5f; emptyTextView.setLayoutParams(layoutParams1); FrameLayout frameLayout = new FrameLayout(context); emptyTextLayout.addView(frameLayout); layoutParams1 = (LinearLayout.LayoutParams) frameLayout.getLayoutParams(); - layoutParams1.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.MATCH_PARENT; layoutParams1.weight = 0.5f; frameLayout.setLayoutParams(layoutParams1); @@ -251,8 +254,8 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter } ((FrameLayout) fragmentView).addView(listView); layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; listView.setLayoutParams(layoutParams); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @@ -290,7 +293,7 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter if (row < 0 || section < 0) { return; } - if (!onlyUsers && section == 0) { + if ((!onlyUsers || chat_id != 0) && section == 0) { if (needPhonebook) { if (row == 0) { try { @@ -302,6 +305,10 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter FileLog.e("tmessages", e); } } + } else if (chat_id != 0) { + if (row == 0) { + presentFragment(new GroupInviteActivity(chat_id)); + } } else { if (row == 0) { if (!MessagesController.isFeatureEnabled("chat_create", ContactsActivity.this)) { @@ -369,7 +376,7 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } } } @@ -420,7 +427,7 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams)editText.getLayoutParams(); if (layoutParams != null) { if (layoutParams instanceof FrameLayout.LayoutParams) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/CountrySelectActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/CountrySelectActivity.java index 2eb56da69..4bc604486 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/CountrySelectActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/CountrySelectActivity.java @@ -32,6 +32,7 @@ import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.LetterSectionsListView; public class CountrySelectActivity extends BaseFragment { @@ -134,8 +135,8 @@ public class CountrySelectActivity extends BaseFragment { emptyTextLayout.setOrientation(LinearLayout.VERTICAL); ((FrameLayout) fragmentView).addView(emptyTextLayout); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) emptyTextLayout.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP; emptyTextLayout.setLayoutParams(layoutParams); emptyTextLayout.setOnTouchListener(new View.OnTouchListener() { @@ -152,16 +153,16 @@ public class CountrySelectActivity extends BaseFragment { emptyTextView.setText(LocaleController.getString("NoResult", R.string.NoResult)); emptyTextLayout.addView(emptyTextView); LinearLayout.LayoutParams layoutParams1 = (LinearLayout.LayoutParams) emptyTextView.getLayoutParams(); - layoutParams1.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.MATCH_PARENT; layoutParams1.weight = 0.5f; emptyTextView.setLayoutParams(layoutParams1); FrameLayout frameLayout = new FrameLayout(context); emptyTextLayout.addView(frameLayout); layoutParams1 = (LinearLayout.LayoutParams) frameLayout.getLayoutParams(); - layoutParams1.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.MATCH_PARENT; layoutParams1.weight = 0.5f; frameLayout.setLayoutParams(layoutParams1); @@ -179,8 +180,8 @@ public class CountrySelectActivity extends BaseFragment { } ((FrameLayout) fragmentView).addView(listView); layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; listView.setLayoutParams(layoutParams); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DocumentSelectActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/DocumentSelectActivity.java index 1daa1499b..260092182 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DocumentSelectActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DocumentSelectActivity.java @@ -8,6 +8,7 @@ package org.telegram.ui; +import android.annotation.SuppressLint; import android.app.AlertDialog; import android.content.BroadcastReceiver; import android.content.Context; @@ -32,14 +33,14 @@ import org.telegram.android.AndroidUtilities; import org.telegram.messenger.FileLog; import org.telegram.android.LocaleController; import org.telegram.messenger.R; -import org.telegram.messenger.Utilities; import org.telegram.ui.Adapters.BaseFragmentAdapter; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; -import org.telegram.ui.AnimationCompat.AnimatorSetProxy; -import org.telegram.ui.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.AnimatorSetProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.Cells.SharedDocumentCell; +import org.telegram.ui.Components.LayoutHelper; import java.io.BufferedReader; import java.io.File; @@ -188,7 +189,7 @@ public class DocumentSelectActivity extends BaseFragment { LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) selectedMessagesCountTextView.getLayoutParams(); layoutParams.weight = 1; layoutParams.width = 0; - layoutParams.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; selectedMessagesCountTextView.setLayoutParams(layoutParams); actionModeViews.add(actionMode.addItem(done, R.drawable.ic_ab_done_gray, R.drawable.bar_selector_mode, null, AndroidUtilities.dp(54))); @@ -233,7 +234,7 @@ public class DocumentSelectActivity extends BaseFragment { } if (sizeLimit != 0) { if (file.length() > sizeLimit) { - showErrorBox(LocaleController.formatString("FileUploadLimit", R.string.FileUploadLimit, Utilities.formatFileSize(sizeLimit))); + showErrorBox(LocaleController.formatString("FileUploadLimit", R.string.FileUploadLimit, AndroidUtilities.formatFileSize(sizeLimit))); return false; } } @@ -297,7 +298,7 @@ public class DocumentSelectActivity extends BaseFragment { he.scrollItem = listView.getFirstVisiblePosition(); he.scrollOffset = listView.getChildAt(0).getTop(); he.dir = currentDir; - he.title = actionBar.getTitle().toString(); + he.title = actionBar.getTitle(); history.add(he); if (!listFiles(file)) { history.remove(he); @@ -312,7 +313,7 @@ public class DocumentSelectActivity extends BaseFragment { } if (sizeLimit != 0) { if (file.length() > sizeLimit) { - showErrorBox(LocaleController.formatString("FileUploadLimit", R.string.FileUploadLimit, Utilities.formatFileSize(sizeLimit))); + showErrorBox(LocaleController.formatString("FileUploadLimit", R.string.FileUploadLimit, AndroidUtilities.formatFileSize(sizeLimit))); return; } } @@ -403,7 +404,7 @@ public class DocumentSelectActivity extends BaseFragment { return false; } emptyView.setText(LocaleController.getString("NoFiles", R.string.NoFiles)); - File[] files = null; + File[] files; try { files = dir.listFiles(); } catch(Exception e) { @@ -448,7 +449,7 @@ public class DocumentSelectActivity extends BaseFragment { String fname = file.getName(); String[] sp = fname.split("\\."); item.ext = sp.length > 1 ? sp[sp.length - 1] : "?"; - item.subtitle = Utilities.formatFileSize(file.length()); + item.subtitle = AndroidUtilities.formatFileSize(file.length()); fname = fname.toLowerCase(); if (fname.endsWith(".jpg") || fname.endsWith(".png") || fname.endsWith(".gif") || fname.endsWith(".jpeg")) { item.thumb = file.getAbsolutePath(); @@ -484,6 +485,7 @@ public class DocumentSelectActivity extends BaseFragment { new AlertDialog.Builder(getParentActivity()).setTitle(LocaleController.getString("AppName", R.string.AppName)).setMessage(error).setPositiveButton(LocaleController.getString("OK", R.string.OK), null).show(); } + @SuppressLint("NewApi") private void listRoots() { currentDir = null; items.clear(); @@ -581,7 +583,7 @@ public class DocumentSelectActivity extends BaseFragment { if (total == 0) { return ""; } - return LocaleController.formatString("FreeOfTotal", R.string.FreeOfTotal, Utilities.formatFileSize(free), Utilities.formatFileSize(total)); + return LocaleController.formatString("FreeOfTotal", R.string.FreeOfTotal, AndroidUtilities.formatFileSize(free), AndroidUtilities.formatFileSize(total)); } private class ListAdapter extends BaseFragmentAdapter { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java index 5f18cf414..d26744043 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java @@ -54,6 +54,7 @@ import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.Cells.UserCell; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.LetterSectionsListView; import java.util.ArrayList; @@ -187,7 +188,7 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen searchListViewAdapter = new SearchAdapter(context, null, false); searchListViewAdapter.setCheckedMap(selectedContacts); searchListViewAdapter.setUseUserCell(true); - listViewAdapter = new ContactsAdapter(context, true, false, null); + listViewAdapter = new ContactsAdapter(context, true, false, null, false); listViewAdapter.setCheckedMap(selectedContacts); fragmentView = new LinearLayout(context); @@ -197,8 +198,8 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen FrameLayout frameLayout = new FrameLayout(context); linearLayout.addView(frameLayout); LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) frameLayout.getLayoutParams(); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.TOP; frameLayout.setLayoutParams(layoutParams); @@ -219,8 +220,8 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen AndroidUtilities.clearCursorDrawable(userSelectEditText); frameLayout.addView(userSelectEditText); FrameLayout.LayoutParams layoutParams1 = (FrameLayout.LayoutParams) userSelectEditText.getLayoutParams(); - layoutParams1.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; layoutParams1.leftMargin = AndroidUtilities.dp(10); layoutParams1.rightMargin = AndroidUtilities.dp(10); layoutParams1.gravity = Gravity.TOP; @@ -325,8 +326,8 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen emptyTextLayout.setOrientation(LinearLayout.VERTICAL); linearLayout.addView(emptyTextLayout); layoutParams = (LinearLayout.LayoutParams) emptyTextLayout.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; emptyTextLayout.setLayoutParams(layoutParams); emptyTextLayout.setOnTouchListener(new View.OnTouchListener() { @Override @@ -342,16 +343,16 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen emptyTextView.setText(LocaleController.getString("NoContacts", R.string.NoContacts)); emptyTextLayout.addView(emptyTextView); layoutParams = (LinearLayout.LayoutParams) emptyTextView.getLayoutParams(); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.weight = 0.5f; emptyTextView.setLayoutParams(layoutParams); FrameLayout frameLayout2 = new FrameLayout(context); emptyTextLayout.addView(frameLayout2); layoutParams = (LinearLayout.LayoutParams) frameLayout2.getLayoutParams(); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.weight = 0.5f; frameLayout2.setLayoutParams(layoutParams); @@ -369,13 +370,13 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen } linearLayout.addView(listView); layoutParams = (LinearLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; listView.setLayoutParams(layoutParams); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView adapterView, View view, int i, long l) { - TLRPC.User user = null; + TLRPC.User user; if (searching && searchWas) { user = searchListViewAdapter.getItem(i); } else { @@ -481,12 +482,7 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen updateVisibleRows(mask); } } else if (id == NotificationCenter.chatDidCreated) { - AndroidUtilities.runOnUIThread(new Runnable() { - @Override - public void run() { - removeSelfFromStack(); - } - }); + removeSelfFromStack(); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java index 6436ef0ce..93aaffa07 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java @@ -47,6 +47,7 @@ import org.telegram.ui.Components.AvatarUpdater; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.Components.FrameLayoutFixed; +import org.telegram.ui.Components.LayoutHelper; import java.util.ArrayList; import java.util.concurrent.Semaphore; @@ -203,8 +204,8 @@ public class GroupCreateFinalActivity extends BaseFragment implements Notificati FrameLayout frameLayout = new FrameLayoutFixed(context); linearLayout.addView(frameLayout); LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) frameLayout.getLayoutParams(); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; frameLayout.setLayoutParams(layoutParams); @@ -254,7 +255,7 @@ public class GroupCreateFinalActivity extends BaseFragment implements Notificati } } }); - showAlertDialog(builder); + showDialog(builder.create()); } }); } @@ -276,8 +277,8 @@ public class GroupCreateFinalActivity extends BaseFragment implements Notificati nameTextView.setTextColor(0xff212121); frameLayout.addView(nameTextView); layoutParams1 = (FrameLayout.LayoutParams) nameTextView.getLayoutParams(); - layoutParams1.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; layoutParams1.leftMargin = LocaleController.isRTL ? AndroidUtilities.dp(16) : AndroidUtilities.dp(96); layoutParams1.rightMargin = LocaleController.isRTL ? AndroidUtilities.dp(96) : AndroidUtilities.dp(16); layoutParams1.gravity = Gravity.CENTER_VERTICAL; @@ -313,8 +314,8 @@ public class GroupCreateFinalActivity extends BaseFragment implements Notificati listView.setAdapter(listAdapter = new ListAdapter(context)); linearLayout.addView(listView); layoutParams = (LinearLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; listView.setLayoutParams(layoutParams); return fragmentView; @@ -369,6 +370,12 @@ public class GroupCreateFinalActivity extends BaseFragment implements Notificati } } + @Override + public void onOpenAnimationEnd() { + nameTextView.requestFocus(); + AndroidUtilities.showKeyboard(nameTextView); + } + @Override public void didReceivedNotification(int id, final Object... args) { if (id == NotificationCenter.updateInterfaces) { @@ -386,26 +393,21 @@ public class GroupCreateFinalActivity extends BaseFragment implements Notificati } donePressed = false; } else if (id == NotificationCenter.chatDidCreated) { - AndroidUtilities.runOnUIThread(new Runnable() { - @Override - public void run() { - if (progressDialog != null) { - try { - progressDialog.dismiss(); - } catch (Exception e) { - FileLog.e("tmessages", e); - } - } - int chat_id = (Integer)args[0]; - NotificationCenter.getInstance().postNotificationName(NotificationCenter.closeChats); - Bundle args2 = new Bundle(); - args2.putInt("chat_id", chat_id); - presentFragment(new ChatActivity(args2), true); - if (uploadedAvatar != null) { - MessagesController.getInstance().changeChatAvatar(chat_id, uploadedAvatar); - } + if (progressDialog != null) { + try { + progressDialog.dismiss(); + } catch (Exception e) { + FileLog.e("tmessages", e); } - }); + } + int chat_id = (Integer)args[0]; + NotificationCenter.getInstance().postNotificationName(NotificationCenter.closeChats); + Bundle args2 = new Bundle(); + args2.putInt("chat_id", chat_id); + presentFragment(new ChatActivity(args2), true); + if (uploadedAvatar != null) { + MessagesController.getInstance().changeChatAvatar(chat_id, uploadedAvatar); + } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GroupInviteActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/GroupInviteActivity.java new file mode 100644 index 000000000..1ea12de42 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupInviteActivity.java @@ -0,0 +1,339 @@ +/* + * This is the source code of Telegram for Android v. 2.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-2015. + */ + +package org.telegram.ui; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Build; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.FrameLayout; +import android.widget.ListView; +import android.widget.ProgressBar; +import android.widget.Toast; + +import org.telegram.android.AndroidUtilities; +import org.telegram.android.LocaleController; +import org.telegram.android.MessagesController; +import org.telegram.android.NotificationCenter; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.ConnectionsManager; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.R; +import org.telegram.messenger.RPCRequest; +import org.telegram.messenger.TLObject; +import org.telegram.messenger.TLRPC; +import org.telegram.ui.ActionBar.ActionBar; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Adapters.BaseFragmentAdapter; +import org.telegram.ui.Cells.TextBlockCell; +import org.telegram.ui.Cells.TextInfoPrivacyCell; +import org.telegram.ui.Cells.TextSettingsCell; +import org.telegram.ui.Components.LayoutHelper; + +public class GroupInviteActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { + + private ListAdapter listAdapter; + + private int chat_id; + private boolean loading; + private TLRPC.ExportedChatInvite invite; + + private int linkRow; + private int linkInfoRow; + private int copyLinkRow; + private int revokeLinkRow; + private int shareLinkRow; + private int shadowRow; + private int rowCount; + + public GroupInviteActivity(int cid) { + super(); + chat_id = cid; + } + + @Override + public boolean onFragmentCreate() { + super.onFragmentCreate(); + + NotificationCenter.getInstance().addObserver(this, NotificationCenter.chatInfoDidLoaded); + MessagesController.getInstance().loadFullChat(chat_id, classGuid, true); + loading = true; + + rowCount = 0; + linkRow = rowCount++; + linkInfoRow = rowCount++; + copyLinkRow = rowCount++; + revokeLinkRow = rowCount++; + shareLinkRow = rowCount++; + shadowRow = rowCount++; + + return true; + } + + @Override + public void onFragmentDestroy() { + NotificationCenter.getInstance().removeObserver(this, NotificationCenter.chatInfoDidLoaded); + } + + @Override + public View createView(Context context, LayoutInflater inflater) { + actionBar.setBackButtonImage(R.drawable.ic_ab_back); + actionBar.setAllowOverlayTitle(true); + actionBar.setTitle(LocaleController.getString("InviteLink", R.string.InviteLink)); + actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { + @Override + public void onItemClick(int id) { + if (id == -1) { + finishFragment(); + } + } + }); + + listAdapter = new ListAdapter(context); + + fragmentView = new FrameLayout(context); + FrameLayout frameLayout = (FrameLayout) fragmentView; + frameLayout.setBackgroundColor(0xfff0f0f0); + + FrameLayout progressView = new FrameLayout(context); + frameLayout.addView(progressView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + + ProgressBar progressBar = new ProgressBar(context); + progressView.addView(progressBar, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); + + ListView listView = new ListView(context); + listView.setDivider(null); + listView.setDividerHeight(0); + listView.setEmptyView(progressView); + listView.setVerticalScrollBarEnabled(false); + listView.setDrawSelectorOnTop(true); + frameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT)); + listView.setAdapter(listAdapter); + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView adapterView, View view, final int i, long l) { + if (getParentActivity() == null) { + return; + } + if (i == copyLinkRow || i == linkRow) { + if (invite == null) { + return; + } + try { + if (Build.VERSION.SDK_INT < 11) { + android.text.ClipboardManager clipboard = (android.text.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE); + clipboard.setText(invite.link); + } else { + android.content.ClipboardManager clipboard = (android.content.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE); + android.content.ClipData clip = android.content.ClipData.newPlainText("label", invite.link); + clipboard.setPrimaryClip(clip); + } + Toast.makeText(getParentActivity(), LocaleController.getString("LinkCopied", R.string.LinkCopied), Toast.LENGTH_SHORT).show(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } else if (i == shareLinkRow) { + if (invite == null) { + return; + } + try { + Intent intent = new Intent(Intent.ACTION_SEND); + intent.setType("text/plain"); + intent.putExtra(Intent.EXTRA_TEXT, invite.link); + getParentActivity().startActivityForResult(Intent.createChooser(intent, LocaleController.getString("InviteToGroupByLink", R.string.InviteToGroupByLink)), 500); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } else if (i == revokeLinkRow) { + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + builder.setMessage(LocaleController.getString("RevokeAlert", R.string.RevokeAlert)); + builder.setTitle(LocaleController.getString("RevokeLink", R.string.RevokeLink)); + builder.setPositiveButton(LocaleController.getString("RevokeButton", R.string.RevokeButton), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + generateLink(true); + } + }); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + showDialog(builder.create()); + } + } + }); + + return fragmentView; + } + + @Override + public void didReceivedNotification(int id, Object... args) { + if (id == NotificationCenter.chatInfoDidLoaded) { + if (args.length != 3) { + return; + } + int cid = (int) args[0]; + int guid = (int) args[2]; + if (cid == chat_id && guid == classGuid) { + invite = MessagesController.getInstance().getExportedInvite(chat_id); + if (!(invite instanceof TLRPC.TL_chatInviteExported)) { + generateLink(false); + } else { + loading = false; + if (listAdapter != null) { + listAdapter.notifyDataSetChanged(); + } + } + } + } + } + + @Override + public void onResume() { + super.onResume(); + if (listAdapter != null) { + listAdapter.notifyDataSetChanged(); + } + } + + private void generateLink(final boolean request) { + loading = true; + TLRPC.TL_messages_exportChatInvite req = new TLRPC.TL_messages_exportChatInvite(); + req.chat_id = chat_id; + final long 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 (error == null) { + invite = (TLRPC.ExportedChatInvite) response; + if (request) { + if (getParentActivity() == null) { + return; + } + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + builder.setMessage(LocaleController.getString("RevokeAlertNewLink", R.string.RevokeAlertNewLink)); + builder.setTitle(LocaleController.getString("RevokeLink", R.string.RevokeLink)); + builder.setNegativeButton(LocaleController.getString("OK", R.string.OK), null); + showDialog(builder.create()); + } + } + loading = false; + listAdapter.notifyDataSetChanged(); + } + }); + } + }); + ConnectionsManager.getInstance().bindRequestToGuid(reqId, classGuid); + if (listAdapter != null) { + listAdapter.notifyDataSetChanged(); + } + } + + private class ListAdapter extends BaseFragmentAdapter { + private Context mContext; + + public ListAdapter(Context context) { + mContext = context; + } + + @Override + public boolean areAllItemsEnabled() { + return false; + } + + @Override + public boolean isEnabled(int i) { + return i == revokeLinkRow || i == copyLinkRow || i == shareLinkRow || i == linkRow; + } + + @Override + public int getCount() { + return loading ? 0 : rowCount; + } + + @Override + public Object getItem(int i) { + return null; + } + + @Override + public long getItemId(int i) { + return i; + } + + @Override + public boolean hasStableIds() { + return false; + } + + @Override + public View getView(int i, View view, ViewGroup viewGroup) { + int type = getItemViewType(i); + if (type == 0) { + if (view == null) { + view = new TextSettingsCell(mContext); + view.setBackgroundColor(0xffffffff); + } + TextSettingsCell textCell = (TextSettingsCell) view; + if (i == copyLinkRow) { + textCell.setText(LocaleController.getString("CopyLink", R.string.CopyLink), true); + } else if (i == shareLinkRow) { + textCell.setText(LocaleController.getString("ShareLink", R.string.ShareLink), false); + } else if (i == revokeLinkRow) { + textCell.setText(LocaleController.getString("RevokeLink", R.string.RevokeLink), true); + } + } else if (type == 1) { + if (view == null) { + view = new TextInfoPrivacyCell(mContext); + } + if (i == shadowRow) { + ((TextInfoPrivacyCell) view).setText(""); + view.setBackgroundResource(R.drawable.greydivider_bottom); + } else if (i == linkInfoRow) { + ((TextInfoPrivacyCell) view).setText(LocaleController.getString("LinkInfo", R.string.LinkInfo)); + view.setBackgroundResource(R.drawable.greydivider); + } + } else if (type == 2) { + if (view == null) { + view = new TextBlockCell(mContext); + view.setBackgroundColor(0xffffffff); + } + ((TextBlockCell) view).setText(invite != null ? invite.link : "error", false); + } + return view; + } + + @Override + public int getItemViewType(int i) { + if (i == copyLinkRow || i == shareLinkRow || i == revokeLinkRow) { + return 0; + } else if (i == shadowRow || i == linkInfoRow) { + return 1; + } else if (i == linkRow) { + return 2; + } + return 0; + } + + @Override + public int getViewTypeCount() { + return 3; + } + + @Override + public boolean isEmpty() { + return loading; + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/IntroActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/IntroActivity.java index 2d3599521..57766ebc6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/IntroActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/IntroActivity.java @@ -30,7 +30,6 @@ import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; import org.telegram.messenger.R; -import org.telegram.messenger.Utilities; public class IntroActivity extends Activity { private ViewPager viewPager; @@ -236,8 +235,14 @@ public class IntroActivity extends Activity { } justCreated = false; } - Utilities.checkForCrashes(this); - Utilities.checkForUpdates(this); + AndroidUtilities.checkForCrashes(this); + AndroidUtilities.checkForUpdates(this); + } + + @Override + protected void onPause() { + super.onPause(); + AndroidUtilities.unregisterUpdates(); } private class IntroAdapter extends PagerAdapter { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LanguageSelectActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LanguageSelectActivity.java index a6efe56be..f7f1bd2d1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LanguageSelectActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LanguageSelectActivity.java @@ -35,6 +35,7 @@ import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; import java.util.ArrayList; import java.util.Timer; @@ -113,8 +114,8 @@ public class LanguageSelectActivity extends BaseFragment { emptyTextLayout.setOrientation(LinearLayout.VERTICAL); ((FrameLayout) fragmentView).addView(emptyTextLayout); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) emptyTextLayout.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP; emptyTextLayout.setLayoutParams(layoutParams); emptyTextLayout.setOnTouchListener(new View.OnTouchListener() { @@ -131,16 +132,16 @@ public class LanguageSelectActivity extends BaseFragment { emptyTextView.setText(LocaleController.getString("NoResult", R.string.NoResult)); emptyTextLayout.addView(emptyTextView); LinearLayout.LayoutParams layoutParams1 = (LinearLayout.LayoutParams) emptyTextView.getLayoutParams(); - layoutParams1.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.MATCH_PARENT; layoutParams1.weight = 0.5f; emptyTextView.setLayoutParams(layoutParams1); FrameLayout frameLayout = new FrameLayout(context); emptyTextLayout.addView(frameLayout); layoutParams1 = (LinearLayout.LayoutParams) frameLayout.getLayoutParams(); - layoutParams1.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.MATCH_PARENT; layoutParams1.weight = 0.5f; frameLayout.setLayoutParams(layoutParams1); @@ -152,8 +153,8 @@ public class LanguageSelectActivity extends BaseFragment { listView.setAdapter(listAdapter); ((FrameLayout) fragmentView).addView(listView); layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; listView.setLayoutParams(layoutParams); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @@ -214,7 +215,7 @@ public class LanguageSelectActivity extends BaseFragment { } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); return true; } }); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LastSeenActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LastSeenActivity.java index b2c6096be..1b1f81c04 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LastSeenActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LastSeenActivity.java @@ -46,6 +46,7 @@ import org.telegram.ui.Adapters.BaseFragmentAdapter; import org.telegram.ui.Cells.HeaderCell; import org.telegram.ui.Cells.TextInfoPrivacyCell; import org.telegram.ui.Cells.TextSettingsCell; +import org.telegram.ui.Components.LayoutHelper; import java.util.ArrayList; @@ -128,7 +129,7 @@ public class LastSeenActivity extends BaseFragment implements NotificationCenter } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); return; } } @@ -154,8 +155,8 @@ public class LastSeenActivity extends BaseFragment implements NotificationCenter listView.setDrawSelectorOnTop(true); frameLayout.addView(listView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP; listView.setLayoutParams(layoutParams); listView.setAdapter(listAdapter); @@ -178,7 +179,7 @@ public class LastSeenActivity extends BaseFragment implements NotificationCenter currentType = newType; updateRows(); } else if (i == neverShareRow || i == alwaysShareRow) { - ArrayList createFromArray = null; + ArrayList createFromArray; if (i == neverShareRow) { createFromArray = currentMinus; } else { @@ -322,7 +323,7 @@ public class LastSeenActivity extends BaseFragment implements NotificationCenter builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); builder.setMessage(LocaleController.getString("PrivacyFloodControlError", R.string.PrivacyFloodControlError)); builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null); - showAlertDialog(builder); + showDialog(builder.create()); } private void checkPrivacy() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LastSeenUsersActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LastSeenUsersActivity.java index a0ce4a77e..b476860fc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LastSeenUsersActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LastSeenUsersActivity.java @@ -35,6 +35,7 @@ import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.Adapters.BaseFragmentAdapter; import org.telegram.ui.Cells.TextInfoCell; import org.telegram.ui.Cells.UserCell; +import org.telegram.ui.Components.LayoutHelper; import java.util.ArrayList; @@ -126,8 +127,8 @@ public class LastSeenUsersActivity extends BaseFragment implements NotificationC emptyTextView.setText(LocaleController.getString("NoContacts", R.string.NoContacts)); frameLayout.addView(emptyTextView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) emptyTextView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP; emptyTextView.setLayoutParams(layoutParams); emptyTextView.setOnTouchListener(new View.OnTouchListener() { @@ -148,8 +149,8 @@ public class LastSeenUsersActivity extends BaseFragment implements NotificationC } frameLayout.addView(listView); layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; listView.setLayoutParams(layoutParams); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @@ -185,7 +186,7 @@ public class LastSeenUsersActivity extends BaseFragment implements NotificationC } } }); - showAlertDialog(builder); + showDialog(builder.create()); return true; } }); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java index e75d44830..42abf382a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java @@ -9,6 +9,7 @@ package org.telegram.ui; import android.app.Activity; +import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.ContentResolver; import android.content.DialogInterface; @@ -44,6 +45,7 @@ import org.telegram.android.ContactsController; import org.telegram.android.MessagesController; import org.telegram.android.MessagesStorage; import org.telegram.android.SendMessagesHelper; +import org.telegram.android.query.StickersQuery; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.FileLog; @@ -54,11 +56,11 @@ 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.Adapters.DrawerLayoutAdapter; import org.telegram.ui.ActionBar.ActionBarLayout; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.DrawerLayoutContainer; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.PasscodeView; import java.io.BufferedReader; @@ -88,9 +90,10 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa private FrameLayout shadowTablet; private FrameLayout shadowTabletSide; private ImageView backgroundTablet; - private DrawerLayoutContainer drawerLayoutContainer; + protected DrawerLayoutContainer drawerLayoutContainer; private DrawerLayoutAdapter drawerLayoutAdapter; private PasscodeView passcodeView; + private AlertDialog visibleDialog; private Intent passcodeSaveIntent; private boolean passcodeSaveIntentIsNew; @@ -128,6 +131,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa setTheme(R.style.Theme_TMessages); getWindow().setBackgroundDrawableResource(R.drawable.transparent); + super.onCreate(savedInstanceState); if (UserConfig.passcodeHash.length() != 0 && UserConfig.appLocked) { @@ -150,8 +154,8 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa RelativeLayout launchLayout = new RelativeLayout(this); drawerLayoutContainer.addView(launchLayout); FrameLayout.LayoutParams layoutParams1 = (FrameLayout.LayoutParams) launchLayout.getLayoutParams(); - layoutParams1.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.MATCH_PARENT; launchLayout.setLayoutParams(layoutParams1); backgroundTablet = new ImageView(this); @@ -159,21 +163,21 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa backgroundTablet.setImageResource(R.drawable.cats); launchLayout.addView(backgroundTablet); RelativeLayout.LayoutParams relativeLayoutParams = (RelativeLayout.LayoutParams) backgroundTablet.getLayoutParams(); - relativeLayoutParams.width = RelativeLayout.LayoutParams.MATCH_PARENT; - relativeLayoutParams.height = RelativeLayout.LayoutParams.MATCH_PARENT; + relativeLayoutParams.width = LayoutHelper.MATCH_PARENT; + relativeLayoutParams.height = LayoutHelper.MATCH_PARENT; backgroundTablet.setLayoutParams(relativeLayoutParams); launchLayout.addView(actionBarLayout); relativeLayoutParams = (RelativeLayout.LayoutParams) actionBarLayout.getLayoutParams(); - relativeLayoutParams.width = RelativeLayout.LayoutParams.MATCH_PARENT; - relativeLayoutParams.height = RelativeLayout.LayoutParams.MATCH_PARENT; + relativeLayoutParams.width = LayoutHelper.MATCH_PARENT; + relativeLayoutParams.height = LayoutHelper.MATCH_PARENT; actionBarLayout.setLayoutParams(relativeLayoutParams); rightActionBarLayout = new ActionBarLayout(this); launchLayout.addView(rightActionBarLayout); relativeLayoutParams = (RelativeLayout.LayoutParams)rightActionBarLayout.getLayoutParams(); relativeLayoutParams.width = AndroidUtilities.dp(320); - relativeLayoutParams.height = RelativeLayout.LayoutParams.MATCH_PARENT; + relativeLayoutParams.height = LayoutHelper.MATCH_PARENT; rightActionBarLayout.setLayoutParams(relativeLayoutParams); rightActionBarLayout.init(rightFragmentsStack); rightActionBarLayout.setDelegate(this); @@ -183,7 +187,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa launchLayout.addView(shadowTabletSide); relativeLayoutParams = (RelativeLayout.LayoutParams) shadowTabletSide.getLayoutParams(); relativeLayoutParams.width = AndroidUtilities.dp(1); - relativeLayoutParams.height = RelativeLayout.LayoutParams.MATCH_PARENT; + relativeLayoutParams.height = LayoutHelper.MATCH_PARENT; shadowTabletSide.setLayoutParams(relativeLayoutParams); shadowTablet = new FrameLayout(this); @@ -191,8 +195,8 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa shadowTablet.setBackgroundColor(0x7F000000); launchLayout.addView(shadowTablet); relativeLayoutParams = (RelativeLayout.LayoutParams) shadowTablet.getLayoutParams(); - relativeLayoutParams.width = RelativeLayout.LayoutParams.MATCH_PARENT; - relativeLayoutParams.height = RelativeLayout.LayoutParams.MATCH_PARENT; + relativeLayoutParams.width = LayoutHelper.MATCH_PARENT; + relativeLayoutParams.height = LayoutHelper.MATCH_PARENT; shadowTablet.setLayoutParams(relativeLayoutParams); shadowTablet.setOnTouchListener(new View.OnTouchListener() { @Override @@ -254,7 +258,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)listView.getLayoutParams(); Point screenSize = AndroidUtilities.getRealScreenSize(); layoutParams.width = AndroidUtilities.isTablet() ? AndroidUtilities.dp(320) : Math.min(screenSize.x, screenSize.y) - AndroidUtilities.dp(56); - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; listView.setPadding(0, 0, 0, 0); listView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE); listView.setDivider(null); @@ -324,8 +328,8 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa passcodeView = new PasscodeView(this); drawerLayoutContainer.addView(passcodeView); FrameLayout.LayoutParams layoutParams1 = (FrameLayout.LayoutParams) passcodeView.getLayoutParams(); - layoutParams1.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = LayoutHelper.MATCH_PARENT; passcodeView.setLayoutParams(layoutParams1); NotificationCenter.getInstance().postNotificationName(NotificationCenter.closeOtherAppActivities, this); @@ -337,8 +341,6 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa NotificationCenter.getInstance().addObserver(this, NotificationCenter.didUpdatedConnectionState); if (Build.VERSION.SDK_INT < 14) { NotificationCenter.getInstance().addObserver(this, NotificationCenter.screenStateChanged); - } else { - NotificationCenter.getInstance().addObserver(this, NotificationCenter.appSwitchedToForeground); } if (actionBarLayout.fragmentsStack.isEmpty()) { @@ -399,11 +401,12 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa FileLog.e("tmessages", e); } } else { - boolean allowOpen = false; + boolean allowOpen = true; if (AndroidUtilities.isTablet()) { allowOpen = actionBarLayout.fragmentsStack.size() <= 1 && layersActionBarLayout.fragmentsStack.isEmpty(); - } else { - allowOpen = actionBarLayout.fragmentsStack.size() <= 1; + if (layersActionBarLayout.fragmentsStack.size() == 1 && layersActionBarLayout.fragmentsStack.get(0) instanceof LoginActivity) { + allowOpen = false; + } } if (actionBarLayout.fragmentsStack.size() == 1 && actionBarLayout.fragmentsStack.get(0) instanceof LoginActivity) { allowOpen = false; @@ -475,22 +478,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa if (Intent.ACTION_SEND.equals(intent.getAction())) { boolean error = false; String type = intent.getType(); - if (type != null && (type.equals("text/plain") || type.equals("message/rfc822")) && (intent.getStringExtra(Intent.EXTRA_TEXT) != null || intent.getCharSequenceExtra(Intent.EXTRA_TEXT) != null)) { - String text = intent.getStringExtra(Intent.EXTRA_TEXT); - if (text == null) { - text = intent.getCharSequenceExtra(Intent.EXTRA_TEXT).toString(); - } - String subject = intent.getStringExtra(Intent.EXTRA_SUBJECT); - - if (text != null && text.length() != 0) { - if ((text.startsWith("http://") || text.startsWith("https://")) && subject != null && subject.length() != 0) { - text = subject + "\n" + text; - } - sendingText = text; - } else { - error = true; - } - } else if (type != null && type.equals(ContactsContract.Contacts.CONTENT_VCARD_TYPE)) { + if (type != null && type.equals(ContactsContract.Contacts.CONTENT_VCARD_TYPE)) { try { Uri uri = (Uri) intent.getExtras().get(Intent.EXTRA_STREAM); if (uri != null) { @@ -502,7 +490,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa String nameCharset = null; ArrayList phones = new ArrayList<>(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(stream, "UTF-8")); - String line = null; + String line; while ((line = bufferedReader.readLine()) != null) { String[] args = line.split(":"); if (args.length != 2) { @@ -531,7 +519,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa } name += line; } - byte[] bytes = Utilities.decodeQuotedPrintable(name.getBytes()); + byte[] bytes = AndroidUtilities.decodeQuotedPrintable(name.getBytes()); if (bytes != null && bytes.length != 0) { String decodedName = new String(bytes, nameCharset); if (decodedName != null) { @@ -571,21 +559,40 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa error = true; } } else { + if (type != null && (type.equals("text/plain") || type.equals("message/rfc822")) && (intent.getStringExtra(Intent.EXTRA_TEXT) != null || intent.getCharSequenceExtra(Intent.EXTRA_TEXT) != null)) { + String text = intent.getStringExtra(Intent.EXTRA_TEXT); + if (text == null) { + text = intent.getCharSequenceExtra(Intent.EXTRA_TEXT).toString(); + } + String subject = intent.getStringExtra(Intent.EXTRA_SUBJECT); + + if (text != null && text.length() != 0) { + if ((text.startsWith("http://") || text.startsWith("https://")) && subject != null && subject.length() != 0) { + text = subject + "\n" + text; + } + sendingText = text; + if (sendingText.contains("WhatsApp")) { //who needs this sent from ...? + sendingText = null; + } + } else { + error = true; + } + } Parcelable parcelable = intent.getParcelableExtra(Intent.EXTRA_STREAM); if (parcelable != null) { - String path = null; + String path; if (!(parcelable instanceof Uri)) { parcelable = Uri.parse(parcelable.toString()); } Uri uri = (Uri) parcelable; - if (uri != null && type != null && type.startsWith("image/")) { - String tempPath = Utilities.getPath(uri); + if (uri != null && (type != null && type.startsWith("image/") || uri.toString().toLowerCase().endsWith(".jpg"))) { + String tempPath = AndroidUtilities.getPath(uri); if (photoPathsArray == null) { photoPathsArray = new ArrayList<>(); } photoPathsArray.add(uri); } else { - path = Utilities.getPath(uri); + path = AndroidUtilities.getPath(uri); if (path != null) { if (path.startsWith("file:")) { path = path.replace("file://", ""); @@ -608,12 +615,12 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa documentsMimeType = type; } } - } else { + } else if (sendingText == null) { error = true; } - if (error) { - Toast.makeText(this, "Unsupported content", Toast.LENGTH_SHORT).show(); - } + } + if (error) { + Toast.makeText(this, "Unsupported content", Toast.LENGTH_SHORT).show(); } } else if (intent.getAction().equals(Intent.ACTION_SEND_MULTIPLE)) { boolean error = false; @@ -637,7 +644,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa if (!(parcelable instanceof Uri)) { parcelable = Uri.parse(parcelable.toString()); } - String path = Utilities.getPath((Uri) parcelable); + String path = AndroidUtilities.getPath((Uri) parcelable); String originalPath = parcelable.toString(); if (originalPath == null) { originalPath = path; @@ -669,6 +676,8 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa Uri data = intent.getData(); if (data != null) { String username = null; + String group = null; + String sticker = null; String scheme = data.getScheme(); if (scheme != null) { if ((scheme.equals("http") || scheme.equals("https"))) { @@ -676,68 +685,35 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa if (host.equals("telegram.me")) { String path = data.getPath(); if (path != null && path.length() >= 6) { - username = path.substring(1); + path = path.substring(1); + if (path.startsWith("joinchat/")) { + group = path.replace("joinchat/", ""); + } else if (path.startsWith("addstickers/")) { + sticker = path.replace("addstickers/", ""); + } else { + username = path; + } } } } else if (scheme.equals("tg")) { - String url = data.toString().toLowerCase(); + String url = data.toString(); if (url.startsWith("tg:resolve") || url.startsWith("tg://resolve")) { url = url.replace("tg:resolve", "tg://telegram.org").replace("tg://resolve", "tg://telegram.org"); data = Uri.parse(url); username = data.getQueryParameter("domain"); + } else if (url.startsWith("tg:join") || url.startsWith("tg://join")) { + url = url.replace("tg:join", "tg://telegram.org").replace("tg://join", "tg://telegram.org"); + data = Uri.parse(url); + group = data.getQueryParameter("invite"); + } else if (url.startsWith("tg:addstickers") || url.startsWith("tg://addstickers")) { + url = url.replace("tg:addstickers", "tg://telegram.org").replace("tg://addstickers", "tg://telegram.org"); + data = Uri.parse(url); + sticker = data.getQueryParameter("set"); } } } - if (username != null) { - final ProgressDialog progressDialog = new ProgressDialog(this); - progressDialog.setMessage(LocaleController.getString("Loading", R.string.Loading)); - progressDialog.setCanceledOnTouchOutside(false); - progressDialog.setCancelable(false); - - TLRPC.TL_contacts_resolveUsername req = new TLRPC.TL_contacts_resolveUsername(); - req.username = username; - final long 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 (!LaunchActivity.this.isFinishing()) { - try { - progressDialog.dismiss(); - } catch (Exception e) { - FileLog.e("tmessages", e); - } - if (error == null && actionBarLayout != null) { - TLRPC.User user = (TLRPC.User) response; - MessagesController.getInstance().putUser(user, false); - ArrayList users = new ArrayList<>(); - users.add(user); - MessagesStorage.getInstance().putUsersAndChats(users, null, false, true); - Bundle args = new Bundle(); - args.putInt("user_id", user.id); - ChatActivity fragment = new ChatActivity(args); - NotificationCenter.getInstance().postNotificationName(NotificationCenter.closeChats); - actionBarLayout.presentFragment(fragment, false, true, true); - } - } - } - }); - } - }); - - progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, LocaleController.getString("Cancel", R.string.Cancel), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - ConnectionsManager.getInstance().cancelRpc(reqId, true); - try { - dialog.dismiss(); - } catch (Exception e) { - FileLog.e("tmessages", e); - } - } - }); - progressDialog.show(); + if (username != null || group != null || sticker != null) { + runLinkRequest(username, group, sticker, 0); } else { try { Cursor cursor = getContentResolver().query(intent.getData(), null, null, null, null); @@ -777,15 +753,11 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa } if (push_user_id != 0) { - if (push_user_id == UserConfig.getClientUserId()) { - open_settings = 1; - } else { - Bundle args = new Bundle(); - args.putInt("user_id", push_user_id); - ChatActivity fragment = new ChatActivity(args); - if (actionBarLayout.presentFragment(fragment, false, true, true)) { - pushOpened = true; - } + Bundle args = new Bundle(); + args.putInt("user_id", push_user_id); + ChatActivity fragment = new ChatActivity(args); + if (actionBarLayout.presentFragment(fragment, false, true, true)) { + pushOpened = true; } } else if (push_chat_id != 0) { Bundle args = new Bundle(); @@ -825,7 +797,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa args.putString("selectAlertStringGroup", LocaleController.getString("SendMessagesToGroup", R.string.SendMessagesToGroup)); MessagesActivity fragment = new MessagesActivity(args); fragment.setDelegate(this); - boolean removeLast = false; + boolean removeLast; if (AndroidUtilities.isTablet()) { removeLast = layersActionBarLayout.fragmentsStack.size() > 0 && layersActionBarLayout.fragmentsStack.get(layersActionBarLayout.fragmentsStack.size() - 1) instanceof MessagesActivity; } else { @@ -893,6 +865,196 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa return false; } + private void runLinkRequest(final String username, final String group, final String sticker, final int state) { + final ProgressDialog progressDialog = new ProgressDialog(this); + progressDialog.setMessage(LocaleController.getString("Loading", R.string.Loading)); + progressDialog.setCanceledOnTouchOutside(false); + progressDialog.setCancelable(false); + long requestId = 0; + + if (username != null) { + TLRPC.TL_contacts_resolveUsername req = new TLRPC.TL_contacts_resolveUsername(); + req.username = username; + requestId = 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 (!LaunchActivity.this.isFinishing()) { + try { + progressDialog.dismiss(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + if (error == null && actionBarLayout != null) { + TLRPC.User user = (TLRPC.User) response; + MessagesController.getInstance().putUser(user, false); + ArrayList users = new ArrayList<>(); + users.add(user); + MessagesStorage.getInstance().putUsersAndChats(users, null, false, true); + Bundle args = new Bundle(); + args.putInt("user_id", user.id); + ChatActivity fragment = new ChatActivity(args); + NotificationCenter.getInstance().postNotificationName(NotificationCenter.closeChats); + actionBarLayout.presentFragment(fragment, false, true, true); + } + } + } + }); + } + }); + } else if (group != null) { + if (state == 0) { + final TLRPC.TL_messages_checkChatInvite req = new TLRPC.TL_messages_checkChatInvite(); + req.hash = group; + requestId = 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 (!LaunchActivity.this.isFinishing()) { + try { + progressDialog.dismiss(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + if (error == null && actionBarLayout != null) { + TLRPC.ChatInvite invite = (TLRPC.ChatInvite) response; + if (invite.chat != null && !invite.chat.left) { + MessagesController.getInstance().putChat(invite.chat, false); + ArrayList chats = new ArrayList<>(); + chats.add(invite.chat); + MessagesStorage.getInstance().putUsersAndChats(null, chats, false, true); + Bundle args = new Bundle(); + args.putInt("chat_id", invite.chat.id); + ChatActivity fragment = new ChatActivity(args); + NotificationCenter.getInstance().postNotificationName(NotificationCenter.closeChats); + actionBarLayout.presentFragment(fragment, false, true, true); + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(LaunchActivity.this); + builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); + builder.setMessage(LocaleController.formatString("JoinToGroup", R.string.JoinToGroup, invite.chat != null ? invite.chat.title : invite.title)); + builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + runLinkRequest(username, group, sticker, 1); + } + }); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + showAlertDialog(builder); + } + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(LaunchActivity.this); + builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); + builder.setMessage(LocaleController.getString("JoinToGroupErrorNotExist", R.string.JoinToGroupErrorNotExist)); + builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null); + showAlertDialog(builder); + } + } + } + }); + } + }); + } else if (state == 1) { + TLRPC.TL_messages_importChatInvite req = new TLRPC.TL_messages_importChatInvite(); + req.hash = group; + ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() { + @Override + public void run(final TLObject response, final TLRPC.TL_error error) { + if (error == null) { + TLRPC.Updates updates = (TLRPC.Updates) response; + MessagesController.getInstance().processUpdates(updates, false); + } + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + if (!LaunchActivity.this.isFinishing()) { + try { + progressDialog.dismiss(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + if (error == null) { + if (actionBarLayout != null) { + TLRPC.Updates updates = (TLRPC.Updates) response; + if (!updates.chats.isEmpty()) { + MessagesController.getInstance().putUsers(updates.users, false); + MessagesController.getInstance().putChats(updates.chats, false); + Bundle args = new Bundle(); + args.putInt("chat_id", updates.chats.get(0).id); + ChatActivity fragment = new ChatActivity(args); + NotificationCenter.getInstance().postNotificationName(NotificationCenter.closeChats); + actionBarLayout.presentFragment(fragment, false, true, true); + } + } + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(LaunchActivity.this); + builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); + if (error.text.equals("USERS_TOO_MUCH")) { + builder.setMessage(LocaleController.getString("JoinToGroupErrorFull", R.string.JoinToGroupErrorFull)); + } else { + builder.setMessage(LocaleController.getString("JoinToGroupErrorNotExist", R.string.JoinToGroupErrorNotExist)); + } + builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null); + showAlertDialog(builder); + } + } + } + }); + } + }); + } + } else if (sticker != null) { + if (!mainFragmentsStack.isEmpty()) { + TLRPC.TL_inputStickerSetShortName stickerset = new TLRPC.TL_inputStickerSetShortName(); + stickerset.short_name = sticker; + StickersQuery.loadStickers(mainFragmentsStack.get(0), stickerset); + } + return; + } + + final long reqId = requestId; + progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, LocaleController.getString("Cancel", R.string.Cancel), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + ConnectionsManager.getInstance().cancelRpc(reqId, true); + try { + dialog.dismiss(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + }); + progressDialog.show(); + } + + public AlertDialog showAlertDialog(AlertDialog.Builder builder) { + try { + if (visibleDialog != null) { + visibleDialog.dismiss(); + visibleDialog = null; + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + try { + visibleDialog = builder.show(); + visibleDialog.setCanceledOnTouchOutside(true); + visibleDialog.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + visibleDialog = null; + } + }); + return visibleDialog; + } catch (Exception e) { + FileLog.e("tmessages", e); + } + return null; + } + @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); @@ -935,7 +1097,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa actionBarLayout.addFragmentToStack(fragment, actionBarLayout.fragmentsStack.size() - 1); } - if (!fragment.openVideoEditor(videoPath, true)) { + if (!fragment.openVideoEditor(videoPath, true, false)) { if (!AndroidUtilities.isTablet()) { messageFragment.finishFragment(true); } @@ -945,13 +1107,14 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa SendMessagesHelper.prepareSendingVideo(videoPath, 0, 0, 0, 0, null, dialog_id, null); } } else { + if (sendingText != null) { + SendMessagesHelper.prepareSendingText(sendingText, dialog_id); + } + actionBarLayout.presentFragment(fragment, true); - if (sendingText != null) { - fragment.processSendingText(sendingText); - } if (photoPathsArray != null) { - SendMessagesHelper.prepareSendingPhotos(null, photoPathsArray, dialog_id, null); + SendMessagesHelper.prepareSendingPhotos(null, photoPathsArray, dialog_id, null, null); } if (documentsPathsArray != null || documentsUrisArray != null) { SendMessagesHelper.prepareSendingDocuments(documentsPathsArray, documentsOriginalPathsArray, documentsUrisArray, documentsMimeType, dialog_id, null); @@ -987,8 +1150,6 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa NotificationCenter.getInstance().removeObserver(this, NotificationCenter.didUpdatedConnectionState); if (Build.VERSION.SDK_INT < 14) { NotificationCenter.getInstance().removeObserver(this, NotificationCenter.screenStateChanged); - } else { - NotificationCenter.getInstance().removeObserver(this, NotificationCenter.appSwitchedToForeground); } } @@ -1018,7 +1179,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa relativeLayoutParams = (RelativeLayout.LayoutParams) actionBarLayout.getLayoutParams(); relativeLayoutParams.width = leftWidth; - relativeLayoutParams.height = RelativeLayout.LayoutParams.MATCH_PARENT; + relativeLayoutParams.height = LayoutHelper.MATCH_PARENT; actionBarLayout.setLayoutParams(relativeLayoutParams); relativeLayoutParams = (RelativeLayout.LayoutParams) shadowTabletSide.getLayoutParams(); @@ -1027,7 +1188,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa relativeLayoutParams = (RelativeLayout.LayoutParams) rightActionBarLayout.getLayoutParams(); relativeLayoutParams.width = AndroidUtilities.displaySize.x - leftWidth; - relativeLayoutParams.height = RelativeLayout.LayoutParams.MATCH_PARENT; + relativeLayoutParams.height = LayoutHelper.MATCH_PARENT; relativeLayoutParams.leftMargin = leftWidth; rightActionBarLayout.setLayoutParams(relativeLayoutParams); @@ -1049,8 +1210,8 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa tabletFullSize = true; relativeLayoutParams = (RelativeLayout.LayoutParams) actionBarLayout.getLayoutParams(); - relativeLayoutParams.width = RelativeLayout.LayoutParams.MATCH_PARENT; - relativeLayoutParams.height = RelativeLayout.LayoutParams.MATCH_PARENT; + relativeLayoutParams.width = LayoutHelper.MATCH_PARENT; + relativeLayoutParams.height = LayoutHelper.MATCH_PARENT; actionBarLayout.setLayoutParams(relativeLayoutParams); shadowTabletSide.setVisibility(View.GONE); @@ -1125,12 +1286,21 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa } ApplicationLoader.mainInterfacePaused = true; ConnectionsManager.getInstance().setAppPaused(true, false); + AndroidUtilities.unregisterUpdates(); } @Override protected void onDestroy() { PhotoViewer.getInstance().destroyPhotoViewer(); SecretPhotoViewer.getInstance().destroyPhotoViewer(); + try { + if (visibleDialog != null) { + visibleDialog.dismiss(); + visibleDialog = null; + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } super.onDestroy(); onFinish(); } @@ -1148,11 +1318,14 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa } else { passcodeView.onResume(); } - Utilities.checkForCrashes(this); - Utilities.checkForUpdates(this); + AndroidUtilities.checkForCrashes(this); + AndroidUtilities.checkForUpdates(this); ApplicationLoader.mainInterfacePaused = false; ConnectionsManager.getInstance().setAppPaused(false, false); updateCurrentConnectionState(); + if (PhotoViewer.getInstance().isVisible()) { + PhotoViewer.getInstance().onResume(); + } } @Override @@ -1209,8 +1382,6 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa onPasscodeResume(); } } - } else if (id == NotificationCenter.appSwitchedToForeground) { - onPasscodeResume(); } } @@ -1413,7 +1584,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa @Override public boolean needPresentFragment(BaseFragment fragment, boolean removeLast, boolean forceWithoutAnimation, ActionBarLayout layout) { if (AndroidUtilities.isTablet()) { - drawerLayoutContainer.setAllowOpenDrawer(!(fragment instanceof LoginActivity) && layersActionBarLayout.getVisibility() != View.VISIBLE, true); + drawerLayoutContainer.setAllowOpenDrawer(!(fragment instanceof LoginActivity || fragment instanceof CountrySelectActivity) && layersActionBarLayout.getVisibility() != View.VISIBLE, true); if (fragment instanceof MessagesActivity) { MessagesActivity messagesActivity = (MessagesActivity)fragment; if (messagesActivity.isMainDialogList() && layout != actionBarLayout) { @@ -1494,7 +1665,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa } return true; } else { - drawerLayoutContainer.setAllowOpenDrawer(!(fragment instanceof LoginActivity), false); + drawerLayoutContainer.setAllowOpenDrawer(!(fragment instanceof LoginActivity || fragment instanceof CountrySelectActivity), false); return true; } } @@ -1502,7 +1673,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa @Override public boolean needAddFragmentToStack(BaseFragment fragment, ActionBarLayout layout) { if (AndroidUtilities.isTablet()) { - drawerLayoutContainer.setAllowOpenDrawer(!(fragment instanceof LoginActivity) && layersActionBarLayout.getVisibility() != View.VISIBLE, true); + drawerLayoutContainer.setAllowOpenDrawer(!(fragment instanceof LoginActivity || fragment instanceof CountrySelectActivity) && layersActionBarLayout.getVisibility() != View.VISIBLE, true); if (fragment instanceof MessagesActivity) { MessagesActivity messagesActivity = (MessagesActivity)fragment; if (messagesActivity.isMainDialogList() && layout != actionBarLayout) { @@ -1559,7 +1730,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa } return true; } else { - drawerLayoutContainer.setAllowOpenDrawer(!(fragment instanceof LoginActivity), false); + drawerLayoutContainer.setAllowOpenDrawer(!(fragment instanceof LoginActivity || fragment instanceof CountrySelectActivity), false); return true; } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java index 86e4b1cab..b275de811 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java @@ -8,11 +8,31 @@ package org.telegram.ui; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.StateListAnimator; import android.content.Context; +import android.content.Intent; +import android.graphics.Outline; import android.location.Location; import android.location.LocationManager; +import android.net.Uri; +import android.os.Build; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.Gravity; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; +import android.view.ViewOutlineProvider; +import android.view.WindowManager; +import android.widget.AbsListView; +import android.widget.AdapterView; +import android.widget.EditText; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ListView; import android.widget.TextView; import com.google.android.gms.maps.CameraUpdate; @@ -22,8 +42,8 @@ import com.google.android.gms.maps.MapView; import com.google.android.gms.maps.MapsInitializer; import com.google.android.gms.maps.model.BitmapDescriptorFactory; +import com.google.android.gms.maps.model.CircleOptions; import com.google.android.gms.maps.model.LatLng; -import com.google.android.gms.maps.model.Marker; import com.google.android.gms.maps.model.MarkerOptions; import org.telegram.android.AndroidUtilities; @@ -39,34 +59,68 @@ import org.telegram.messenger.R; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; +import org.telegram.ui.Adapters.BaseLocationAdapter; +import org.telegram.ui.Adapters.LocationActivityAdapter; +import org.telegram.ui.Adapters.LocationActivitySearchAdapter; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.MapPlaceholderDrawable; +import java.util.ArrayList; import java.util.List; +import java.util.Locale; public class LocationActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { private GoogleMap googleMap; private TextView distanceTextView; - private Marker userMarker; - private Location myLocation; - private Location userLocation; - private MessageObject messageObject; private BackupImageView avatarImageView; private TextView nameTextView; + private MapView mapView; + private FrameLayout mapViewClip; + private LocationActivityAdapter adapter; + private ListView listView; + private ListView searchListView; + private LocationActivitySearchAdapter searchAdapter; + private LinearLayout emptyTextLayout; + private ImageView markerImageView; + private ImageView markerXImageView; + private ImageView locationButton; + + private AnimatorSet animatorSet; + + private boolean searching; + private boolean searchWas; + + private boolean wasResults; + + private Location myLocation; + private Location userLocation; + private int markerTop; + + private MessageObject messageObject; private boolean userLocationMoved = false; private boolean firstWas = false; - private MapView mapView; + private CircleOptions circleOptions; private LocationActivityDelegate delegate; - private final static int map_to_my_location = 1; + private int overScrollHeight = AndroidUtilities.displaySize.x - AndroidUtilities.getCurrentActionBarHeight() - AndroidUtilities.dp(66); + private int halfHeight; + + private final static int share = 1; private final static int map_list_menu_map = 2; private final static int map_list_menu_satellite = 3; private final static int map_list_menu_hybrid = 4; public interface LocationActivityDelegate { - void didSelectLocation(double latitude, double longitude); + void didSelectLocation(TLRPC.MessageMedia location); + } + + @Override + public boolean needAddActionBar() { + return messageObject != null; } @Override @@ -88,16 +142,20 @@ public class LocationActivity extends BaseFragment implements NotificationCenter if (mapView != null) { mapView.onDestroy(); } + if (adapter != null) { + adapter.destroy(); + } + if (searchAdapter != null) { + searchAdapter.destroy(); + } } @Override public View createView(Context context, LayoutInflater inflater) { actionBar.setBackButtonImage(R.drawable.ic_ab_back); actionBar.setAllowOverlayTitle(true); - if (messageObject != null) { - actionBar.setTitle(LocaleController.getString("ChatLocation", R.string.ChatLocation)); - } else { - actionBar.setTitle(LocaleController.getString("ShareLocation", R.string.ShareLocation)); + if (AndroidUtilities.isTablet()) { + actionBar.setOccupyStatusBar(false); } actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @@ -117,52 +175,428 @@ public class LocationActivity extends BaseFragment implements NotificationCenter if (googleMap != null) { googleMap.setMapType(GoogleMap.MAP_TYPE_HYBRID); } - } else if (id == map_to_my_location) { - if (myLocation != null) { - LatLng latLng = new LatLng(myLocation.getLatitude(), myLocation.getLongitude()); - if (googleMap != null) { - CameraUpdate position = CameraUpdateFactory.newLatLngZoom(latLng, googleMap.getMaxZoomLevel() - 8); - googleMap.animateCamera(position); - } + } else if (id == share) { + try { + double lat = messageObject.messageOwner.media.geo.lat; + double lon = messageObject.messageOwner.media.geo._long; + getParentActivity().startActivity(new Intent(android.content.Intent.ACTION_VIEW, Uri.parse("geo:" + lat + "," + lon + "?q=" + lat + "," + lon))); + } catch (Exception e) { + FileLog.e("tmessages", e); } } } }); ActionBarMenu menu = actionBar.createMenu(); - menu.addItem(map_to_my_location, R.drawable.ic_ab_location); + if (messageObject != null) { + if (messageObject.messageOwner.media.title != null && messageObject.messageOwner.media.title.length() > 0) { + actionBar.setTitle(messageObject.messageOwner.media.title); + if (messageObject.messageOwner.media.address != null && messageObject.messageOwner.media.address.length() > 0) { + actionBar.setSubtitle(messageObject.messageOwner.media.address); + } + } else { + actionBar.setTitle(LocaleController.getString("ChatLocation", R.string.ChatLocation)); + } + menu.addItem(share, R.drawable.share); + } else { + actionBar.setTitle(LocaleController.getString("ShareLocation", R.string.ShareLocation)); + + ActionBarMenuItem item = menu.addItem(0, R.drawable.ic_ab_search).setIsSearchField(true).setActionBarMenuItemSearchListener(new ActionBarMenuItem.ActionBarMenuItemSearchListener() { + @Override + public void onSearchExpand() { + searching = true; + listView.setVisibility(View.GONE); + mapViewClip.setVisibility(View.GONE); + searchListView.setVisibility(View.VISIBLE); + searchListView.setEmptyView(emptyTextLayout); + } + + @Override + public boolean onSearchCollapse() { + searching = false; + searchWas = false; + searchListView.setEmptyView(null); + listView.setVisibility(View.VISIBLE); + mapViewClip.setVisibility(View.VISIBLE); + searchListView.setVisibility(View.GONE); + emptyTextLayout.setVisibility(View.GONE); + searchAdapter.searchDelayed(null, null); + return true; + } + + @Override + public void onTextChanged(EditText editText) { + if (searchAdapter == null) { + return; + } + String text = editText.getText().toString(); + if (text.length() != 0) { + searchWas = true; + } + searchAdapter.searchDelayed(text, userLocation); + } + }); + item.getSearchField().setHint(LocaleController.getString("Search", R.string.Search)); + } ActionBarMenuItem item = menu.addItem(0, R.drawable.ic_ab_other); item.addSubItem(map_list_menu_map, LocaleController.getString("Map", R.string.Map), 0); item.addSubItem(map_list_menu_satellite, LocaleController.getString("Satellite", R.string.Satellite), 0); item.addSubItem(map_list_menu_hybrid, LocaleController.getString("Hybrid", R.string.Hybrid), 0); + fragmentView = new FrameLayout(context) { + private boolean first = true; + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + + if (changed) { + fixLayoutInternal(first); + first = false; + } + } + }; + FrameLayout frameLayout = (FrameLayout) fragmentView; + + locationButton = new ImageView(context); + locationButton.setBackgroundResource(R.drawable.floating_user_states); + locationButton.setImageResource(R.drawable.myloc_on); + locationButton.setScaleType(ImageView.ScaleType.CENTER); + if (Build.VERSION.SDK_INT >= 21) { + StateListAnimator animator = new StateListAnimator(); + animator.addState(new int[]{android.R.attr.state_pressed}, ObjectAnimator.ofFloat(locationButton, "translationZ", AndroidUtilities.dp(2), AndroidUtilities.dp(4)).setDuration(200)); + animator.addState(new int[]{}, ObjectAnimator.ofFloat(locationButton, "translationZ", AndroidUtilities.dp(4), AndroidUtilities.dp(2)).setDuration(200)); + locationButton.setStateListAnimator(animator); + locationButton.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setOval(0, 0, AndroidUtilities.dp(56), AndroidUtilities.dp(56)); + } + }); + } if (messageObject != null) { - fragmentView = inflater.inflate(R.layout.location_view_layout, null, false); + mapView = new MapView(context); + frameLayout.setBackgroundDrawable(new MapPlaceholderDrawable()); + mapView.onCreate(null); + try { + MapsInitializer.initialize(context); + googleMap = mapView.getMap(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + + FrameLayout bottomView = new FrameLayout(context); + bottomView.setBackgroundResource(R.drawable.location_panel); + frameLayout.addView(bottomView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 60, Gravity.LEFT | Gravity.BOTTOM)); + bottomView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (userLocation != null) { + LatLng latLng = new LatLng(userLocation.getLatitude(), userLocation.getLongitude()); + if (googleMap != null) { + CameraUpdate position = CameraUpdateFactory.newLatLngZoom(latLng, googleMap.getMaxZoomLevel() - 8); + googleMap.animateCamera(position); + } + } + } + }); + + avatarImageView = new BackupImageView(context); + avatarImageView.setRoundRadius(AndroidUtilities.dp(20)); + bottomView.addView(avatarImageView, LayoutHelper.createFrame(40, 40, Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), LocaleController.isRTL ? 0 : 12, 12, LocaleController.isRTL ? 12 : 0, 0)); + + nameTextView = new TextView(context); + nameTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + nameTextView.setTextColor(0xff212121); + nameTextView.setMaxLines(1); + nameTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + nameTextView.setEllipsize(TextUtils.TruncateAt.END); + nameTextView.setSingleLine(true); + nameTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + bottomView.addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), LocaleController.isRTL ? 12 : 72, 10, LocaleController.isRTL ? 72 : 12, 0)); + + distanceTextView = new TextView(context); + distanceTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + distanceTextView.setTextColor(0xff2f8cc9); + distanceTextView.setMaxLines(1); + distanceTextView.setEllipsize(TextUtils.TruncateAt.END); + distanceTextView.setSingleLine(true); + distanceTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + bottomView.addView(distanceTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), LocaleController.isRTL ? 12 : 72, 33, LocaleController.isRTL ? 72 : 12, 0)); + + userLocation = new Location("network"); + userLocation.setLatitude(messageObject.messageOwner.media.geo.lat); + userLocation.setLongitude(messageObject.messageOwner.media.geo._long); + if (googleMap != null) { + LatLng latLng = new LatLng(userLocation.getLatitude(), userLocation.getLongitude()); + try { + googleMap.addMarker(new MarkerOptions().position(latLng).icon(BitmapDescriptorFactory.fromResource(R.drawable.map_pin))); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + CameraUpdate position = CameraUpdateFactory.newLatLngZoom(latLng, googleMap.getMaxZoomLevel() - 8); + googleMap.moveCamera(position); + } + + ImageView routeButton = new ImageView(context); + routeButton.setBackgroundResource(R.drawable.floating_states); + routeButton.setImageResource(R.drawable.navigate); + routeButton.setScaleType(ImageView.ScaleType.CENTER); + if (Build.VERSION.SDK_INT >= 21) { + StateListAnimator animator = new StateListAnimator(); + animator.addState(new int[]{android.R.attr.state_pressed}, ObjectAnimator.ofFloat(routeButton, "translationZ", AndroidUtilities.dp(2), AndroidUtilities.dp(4)).setDuration(200)); + animator.addState(new int[]{}, ObjectAnimator.ofFloat(routeButton, "translationZ", AndroidUtilities.dp(4), AndroidUtilities.dp(2)).setDuration(200)); + routeButton.setStateListAnimator(animator); + routeButton.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setOval(0, 0, AndroidUtilities.dp(56), AndroidUtilities.dp(56)); + } + }); + } + frameLayout.addView(routeButton, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.BOTTOM, LocaleController.isRTL ? 14 : 0, 0, LocaleController.isRTL ? 0 : 14, 28)); + routeButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (myLocation != null) { + try { + Intent intent = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse(String.format(Locale.US, "http://maps.google.com/maps?saddr=%f,%f&daddr=%f,%f", myLocation.getLatitude(), myLocation.getLongitude(), messageObject.messageOwner.media.geo.lat, messageObject.messageOwner.media.geo._long))); + getParentActivity().startActivity(intent); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + } + }); + + frameLayout.addView(locationButton, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.BOTTOM, LocaleController.isRTL ? 14 : 0, 0, LocaleController.isRTL ? 0 : 14, 100)); + locationButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (myLocation != null && googleMap != null) { + googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(myLocation.getLatitude(), myLocation.getLongitude()), googleMap.getMaxZoomLevel() - 8)); + } + } + }); } else { - fragmentView = inflater.inflate(R.layout.location_attach_layout, null, false); - } + searchWas = false; + searching = false; + mapViewClip = new FrameLayout(context); + mapViewClip.setBackgroundDrawable(new MapPlaceholderDrawable()); + if (adapter != null) { + adapter.destroy(); + } + if (searchAdapter != null) { + searchAdapter.destroy(); + } - avatarImageView = (BackupImageView) fragmentView.findViewById(R.id.location_avatar_view); - if (avatarImageView != null) { - avatarImageView.setRoundRadius(AndroidUtilities.dp(32)); - } - nameTextView = (TextView) fragmentView.findViewById(R.id.location_name_label); - distanceTextView = (TextView) fragmentView.findViewById(R.id.location_distance_label); - View bottomView = fragmentView.findViewById(R.id.location_bottom_view); - TextView sendButton = (TextView) fragmentView.findViewById(R.id.location_send_button); - if (sendButton != null) { - sendButton.setText(LocaleController.getString("SendLocation", R.string.SendLocation).toUpperCase()); - sendButton.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - } + listView = new ListView(context); + listView.setAdapter(adapter = new LocationActivityAdapter(context)); + listView.setVerticalScrollBarEnabled(false); + listView.setDividerHeight(0); + listView.setDivider(null); + frameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP)); + listView.setOnScrollListener(new AbsListView.OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { - mapView = (MapView) fragmentView.findViewById(R.id.map_view); - mapView.onCreate(null); - try { - MapsInitializer.initialize(context); - googleMap = mapView.getMap(); - } catch (Exception e) { - FileLog.e("tmessages", e); + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + if (totalItemCount == 0) { + return; + } + updateClipView(firstVisibleItem); + } + }); + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + if (position == 1) { + if (delegate != null && userLocation != null) { + TLRPC.TL_messageMediaGeo location = new TLRPC.TL_messageMediaGeo(); + location.geo = new TLRPC.TL_geoPoint(); + location.geo.lat = userLocation.getLatitude(); + location.geo._long = userLocation.getLongitude(); + delegate.didSelectLocation(location); + } + finishFragment(); + } else { + TLRPC.TL_messageMediaVenue object = adapter.getItem(position); + if (object != null && delegate != null) { + delegate.didSelectLocation(object); + } + finishFragment(); + } + } + }); + adapter.setDelegate(new BaseLocationAdapter.BaseLocationAdapterDelegate() { + @Override + public void didLoadedSearchResult(ArrayList places) { + if (!wasResults && !places.isEmpty()) { + wasResults = true; + } + } + }); + adapter.setOverScrollHeight(overScrollHeight); + + frameLayout.addView(mapViewClip, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP)); + + mapView = new MapView(context) { + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (Build.VERSION.SDK_INT >= 11) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + if (animatorSet != null) { + animatorSet.cancel(); + } + animatorSet = new AnimatorSet(); + animatorSet.setDuration(200); + animatorSet.playTogether( + ObjectAnimator.ofFloat(markerImageView, "translationY", markerTop + -AndroidUtilities.dp(10)), + ObjectAnimator.ofFloat(markerXImageView, "alpha", 1.0f)); + animatorSet.start(); + } else if (ev.getAction() == MotionEvent.ACTION_UP) { + if (animatorSet != null) { + animatorSet.cancel(); + } + animatorSet = new AnimatorSet(); + animatorSet.setDuration(200); + animatorSet.playTogether( + ObjectAnimator.ofFloat(markerImageView, "translationY", markerTop), + ObjectAnimator.ofFloat(markerXImageView, "alpha", 0.0f)); + animatorSet.start(); + } + } + if (ev.getAction() == MotionEvent.ACTION_MOVE) { + if (!userLocationMoved) { + if (Build.VERSION.SDK_INT >= 11) { + AnimatorSet animatorSet = new AnimatorSet(); + animatorSet.setDuration(200); + animatorSet.play(ObjectAnimator.ofFloat(locationButton, "alpha", 1.0f)); + animatorSet.start(); + } else { + locationButton.setVisibility(VISIBLE); + } + userLocationMoved = true; + } + if (googleMap != null && userLocation != null) { + userLocation.setLatitude(googleMap.getCameraPosition().target.latitude); + userLocation.setLongitude(googleMap.getCameraPosition().target.longitude); + } + adapter.setCustomLocation(userLocation); + } + return super.onInterceptTouchEvent(ev); + } + }; + mapView.onCreate(null); + try { + MapsInitializer.initialize(context); + googleMap = mapView.getMap(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + + View shadow = new View(context); + shadow.setBackgroundResource(R.drawable.header_shadow_reverse); + mapViewClip.addView(shadow, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, AndroidUtilities.dp(3), Gravity.LEFT | Gravity.BOTTOM)); + + markerImageView = new ImageView(context); + markerImageView.setImageResource(R.drawable.map_pin); + mapViewClip.addView(markerImageView, LayoutHelper.createFrame(24, 42, Gravity.TOP | Gravity.CENTER_HORIZONTAL)); + + if (Build.VERSION.SDK_INT >= 11) { + markerXImageView = new ImageView(context); + markerXImageView.setAlpha(0.0f); + markerXImageView.setImageResource(R.drawable.place_x); + mapViewClip.addView(markerXImageView, LayoutHelper.createFrame(14, 14, Gravity.TOP | Gravity.CENTER_HORIZONTAL)); + } + + mapViewClip.addView(locationButton, LayoutHelper.createFrame(Build.VERSION.SDK_INT >= 21 ? 56 : 60, Build.VERSION.SDK_INT >= 21 ? 56 : 60, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.BOTTOM, LocaleController.isRTL ? 14 : 0, 0, LocaleController.isRTL ? 0 : 14, 14)); + locationButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (myLocation != null && googleMap != null) { + if (Build.VERSION.SDK_INT >= 11) { + AnimatorSet animatorSet = new AnimatorSet(); + animatorSet.setDuration(200); + animatorSet.play(ObjectAnimator.ofFloat(locationButton, "alpha", 0.0f)); + animatorSet.start(); + } else { + locationButton.setVisibility(View.INVISIBLE); + } + adapter.setCustomLocation(null); + userLocationMoved = false; + googleMap.animateCamera(CameraUpdateFactory.newLatLng(new LatLng(myLocation.getLatitude(), myLocation.getLongitude()))); + } + } + }); + if (Build.VERSION.SDK_INT >= 11) { + locationButton.setAlpha(0.0f); + } else { + locationButton.setVisibility(View.INVISIBLE); + } + + emptyTextLayout = new LinearLayout(context); + emptyTextLayout.setVisibility(View.GONE); + emptyTextLayout.setOrientation(LinearLayout.VERTICAL); + frameLayout.addView(emptyTextLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP)); + emptyTextLayout.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + return true; + } + }); + + TextView emptyTextView = new TextView(context); + emptyTextView.setTextColor(0xff808080); + emptyTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); + emptyTextView.setGravity(Gravity.CENTER); + emptyTextView.setText(LocaleController.getString("NoResult", R.string.NoResult)); + emptyTextLayout.addView(emptyTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0.5f)); + + FrameLayout frameLayoutEmpty = new FrameLayout(context); + emptyTextLayout.addView(frameLayoutEmpty, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0.5f)); + + searchListView = new ListView(context); + searchListView.setVisibility(View.GONE); + searchListView.setDividerHeight(0); + searchListView.setDivider(null); + searchListView.setAdapter(searchAdapter = new LocationActivitySearchAdapter(context)); + frameLayout.addView(searchListView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP)); + searchListView.setOnScrollListener(new AbsListView.OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + if (scrollState == SCROLL_STATE_TOUCH_SCROLL && searching && searchWas) { + AndroidUtilities.hideKeyboard(getParentActivity().getCurrentFocus()); + } + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + + } + }); + searchListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + TLRPC.TL_messageMediaVenue object = searchAdapter.getItem(position); + if (object != null && delegate != null) { + delegate.didSelectLocation(object); + } + finishFragment(); + } + }); + + if (googleMap != null) { + userLocation = new Location("network"); + userLocation.setLatitude(20.659322); + userLocation.setLongitude(-11.406250); + } + + frameLayout.addView(actionBar); } if (googleMap != null) { @@ -176,74 +610,129 @@ public class LocationActivity extends BaseFragment implements NotificationCenter positionMarker(location); } }); - myLocation = getLastLocation(); - - if (sendButton != null) { - userLocation = new Location("network"); - userLocation.setLatitude(20.659322); - userLocation.setLongitude(-11.406250); - LatLng latLng = new LatLng(20.659322, -11.406250); - userMarker = googleMap.addMarker(new MarkerOptions().position(latLng).icon(BitmapDescriptorFactory.fromResource(R.drawable.map_pin)).draggable(true)); - - sendButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if (delegate != null) { - delegate.didSelectLocation(userLocation.getLatitude(), userLocation.getLongitude()); - } - finishFragment(); - } - }); - - googleMap.setOnMarkerDragListener(new GoogleMap.OnMarkerDragListener() { - @Override - public void onMarkerDragStart(Marker marker) { - } - - @Override - public void onMarkerDrag(Marker marker) { - userLocationMoved = true; - } - - @Override - public void onMarkerDragEnd(Marker marker) { - LatLng latLng = marker.getPosition(); - userLocation.setLatitude(latLng.latitude); - userLocation.setLongitude(latLng.longitude); - } - }); - } - - if (bottomView != null) { - bottomView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if (userLocation != null) { - LatLng latLng = new LatLng(userLocation.getLatitude(), userLocation.getLongitude()); - CameraUpdate position = CameraUpdateFactory.newLatLngZoom(latLng, googleMap.getMaxZoomLevel() - 8); - googleMap.animateCamera(position); - } - } - }); - } - - if (messageObject != null) { - userLocation = new Location("network"); - userLocation.setLatitude(messageObject.messageOwner.media.geo.lat); - userLocation.setLongitude(messageObject.messageOwner.media.geo._long); - LatLng latLng = new LatLng(userLocation.getLatitude(), userLocation.getLongitude()); - userMarker = googleMap.addMarker(new MarkerOptions().position(latLng). - icon(BitmapDescriptorFactory.fromResource(R.drawable.map_pin))); - CameraUpdate position = CameraUpdateFactory.newLatLngZoom(latLng, googleMap.getMaxZoomLevel() - 8); - googleMap.moveCamera(position); - } - - positionMarker(myLocation); + positionMarker(myLocation = getLastLocation()); } return fragmentView; } + @Override + public void onOpenAnimationEnd() { + if (mapViewClip != null) { + mapViewClip.addView(mapView, 0, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, overScrollHeight + AndroidUtilities.dp(10), Gravity.TOP | Gravity.LEFT)); + updateClipView(listView.getFirstVisiblePosition()); + } else { + ((FrameLayout) fragmentView).addView(mapView, 0, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT)); + } + } + + private void updateClipView(int firstVisibleItem) { + int height = 0; + int top = 0; + View child = listView.getChildAt(0); + if (child != null) { + if (firstVisibleItem == 0) { + top = child.getTop(); + height = overScrollHeight + (top < 0 ? top : 0); + halfHeight = (top < 0 ? top : 0) / 2; + } + FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mapViewClip.getLayoutParams(); + if (layoutParams != null) { + if (height <= 0) { + if (mapView.getVisibility() == View.VISIBLE) { + mapView.setVisibility(View.INVISIBLE); + mapViewClip.setVisibility(View.INVISIBLE); + } + } else { + if (mapView.getVisibility() == View.INVISIBLE) { + mapView.setVisibility(View.VISIBLE); + mapViewClip.setVisibility(View.VISIBLE); + } + } + if (Build.VERSION.SDK_INT >= 11) { + mapViewClip.setTranslationY(Math.min(0, top)); + mapView.setTranslationY(Math.max(0, -top / 2)); + markerImageView.setTranslationY(markerTop = -top - AndroidUtilities.dp(42) + height / 2); + markerXImageView.setTranslationY(-top - AndroidUtilities.dp(7) + height / 2); + + if (googleMap != null) { + layoutParams = (FrameLayout.LayoutParams) mapView.getLayoutParams(); + if (layoutParams != null && layoutParams.height != overScrollHeight + AndroidUtilities.dp(10)) { + layoutParams.height = overScrollHeight + AndroidUtilities.dp(10); + googleMap.setPadding(0, 0, 0, AndroidUtilities.dp(10)); + mapView.setLayoutParams(layoutParams); + } + } + } else { + markerTop = 0; + layoutParams.height = height; + mapViewClip.setLayoutParams(layoutParams); + + layoutParams = (FrameLayout.LayoutParams) markerImageView.getLayoutParams(); + layoutParams.topMargin = height / 2 - AndroidUtilities.dp(42); + markerImageView.setLayoutParams(layoutParams); + + if (googleMap != null) { + layoutParams = (FrameLayout.LayoutParams) mapView.getLayoutParams(); + if (layoutParams != null) { + layoutParams.topMargin = halfHeight; + layoutParams.height = overScrollHeight + AndroidUtilities.dp(10); + googleMap.setPadding(0, 0, 0, AndroidUtilities.dp(10)); + mapView.setLayoutParams(layoutParams); + } + } + } + } + } + } + + private void fixLayoutInternal(final boolean resume) { + if (listView != null) { + int height = (actionBar.getOccupyStatusBar() ? AndroidUtilities.statusBarHeight : 0) + AndroidUtilities.getCurrentActionBarHeight(); + int viewHeight = fragmentView.getMeasuredHeight(); + if (viewHeight == 0) { + return; + } + overScrollHeight = viewHeight - AndroidUtilities.dp(66) - height; + + FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); + layoutParams.topMargin = height; + listView.setLayoutParams(layoutParams); + layoutParams = (FrameLayout.LayoutParams) mapViewClip.getLayoutParams(); + layoutParams.topMargin = height; + layoutParams.height = overScrollHeight; + mapViewClip.setLayoutParams(layoutParams); + layoutParams = (FrameLayout.LayoutParams) searchListView.getLayoutParams(); + layoutParams.topMargin = height; + searchListView.setLayoutParams(layoutParams); + + adapter.setOverScrollHeight(overScrollHeight); + layoutParams = (FrameLayout.LayoutParams) mapView.getLayoutParams(); + if (layoutParams != null) { + layoutParams.height = overScrollHeight + AndroidUtilities.dp(10); + if (googleMap != null) { + googleMap.setPadding(0, 0, 0, AndroidUtilities.dp(10)); + } + mapView.setLayoutParams(layoutParams); + } + adapter.notifyDataSetChanged(); + + if (resume) { + listView.setSelectionFromTop(0, -(int) (AndroidUtilities.dp(56) * 2.5f + AndroidUtilities.dp(36 + 66))); + updateClipView(listView.getFirstVisiblePosition()); + listView.post(new Runnable() { + @Override + public void run() { + listView.setSelectionFromTop(0, -(int) (AndroidUtilities.dp(56) * 2.5f + AndroidUtilities.dp(36 + 66))); + updateClipView(listView.getFirstVisiblePosition()); + } + }); + } else { + updateClipView(listView.getFirstVisiblePosition()); + } + } + } + private Location getLastLocation() { LocationManager lm = (LocationManager) ApplicationLoader.applicationContext.getSystemService(Context.LOCATION_SERVICE); List providers = lm.getProviders(true); @@ -281,7 +770,7 @@ public class LocationActivity extends BaseFragment implements NotificationCenter if (location == null) { return; } - myLocation = location; + myLocation = new Location(location); if (messageObject != null) { if (userLocation != null && distanceTextView != null) { float distance = location.distanceTo(userLocation); @@ -291,11 +780,14 @@ public class LocationActivity extends BaseFragment implements NotificationCenter distanceTextView.setText(String.format("%.2f %s", distance / 1000.0f, LocaleController.getString("KMetersAway", R.string.KMetersAway))); } } - } else { - if (!userLocationMoved && googleMap != null) { - userLocation = location; - LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude()); - userMarker.setPosition(latLng); + } else if (googleMap != null) { + LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude()); + if (adapter != null) { + adapter.searchGooglePlacesWithQuery(null, myLocation); + adapter.setGpsLocation(myLocation); + } + if (!userLocationMoved) { + userLocation = new Location(location); if (firstWas) { CameraUpdate position = CameraUpdateFactory.newLatLng(latLng); googleMap.animateCamera(position); @@ -339,10 +831,14 @@ public class LocationActivity extends BaseFragment implements NotificationCenter @Override public void onResume() { super.onResume(); + if (!AndroidUtilities.isTablet()) { + getParentActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); + } if (mapView != null) { mapView.onResume(); } updateUserData(); + fixLayoutInternal(true); } @Override @@ -356,4 +852,10 @@ public class LocationActivity extends BaseFragment implements NotificationCenter public void setDelegate(LocationActivityDelegate delegate) { this.delegate = delegate; } + + private void updateSearchInterface() { + if (adapter != null) { + adapter.notifyDataSetChanged(); + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java index b7b6fcf7c..2ae84cf47 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java @@ -9,7 +9,9 @@ package org.telegram.ui; import android.animation.Animator; +import android.annotation.SuppressLint; import android.app.AlertDialog; +import android.app.Dialog; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; @@ -64,6 +66,7 @@ import org.telegram.messenger.Utilities; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.SlideView; import org.telegram.ui.Components.TypefaceSpan; @@ -145,8 +148,8 @@ public class LoginActivity extends BaseFragment { views[a].setVisibility(a == 0 ? View.VISIBLE : View.GONE); frameLayout.addView(views[a]); FrameLayout.LayoutParams layoutParams1 = (FrameLayout.LayoutParams) views[a].getLayoutParams(); - layoutParams1.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams1.height = a == 0 ? FrameLayout.LayoutParams.WRAP_CONTENT : FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams1.width = LayoutHelper.MATCH_PARENT; + layoutParams1.height = a == 0 ? LayoutHelper.WRAP_CONTENT : LayoutHelper.MATCH_PARENT; layoutParams1.leftMargin = AndroidUtilities.dp(AndroidUtilities.isTablet() ? 26 : 18); layoutParams1.rightMargin = AndroidUtilities.dp(AndroidUtilities.isTablet() ? 26 : 18); layoutParams1.topMargin = AndroidUtilities.dp(30); @@ -268,7 +271,7 @@ public class LoginActivity extends BaseFragment { } else if (currentViewNum == 3) { views[currentViewNum].onBackPressed(); setPage(0, true, null, true); - } else if (currentViewNum == 4) { + } else if (currentViewNum == 4) { views[currentViewNum].onBackPressed(); setPage(3, true, null, true); } @@ -283,7 +286,7 @@ public class LoginActivity extends BaseFragment { builder.setTitle(title); builder.setMessage(text); builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null); - showAlertDialog(builder); + showDialog(builder.create()); } public void needShowProgress() { @@ -325,6 +328,7 @@ public class LoginActivity extends BaseFragment { public void onAnimationStart(Animator animator) { } + @SuppressLint("NewApi") @Override public void onAnimationEnd(Animator animator) { outView.setVisibility(View.GONE); @@ -424,11 +428,11 @@ public class LoginActivity extends BaseFragment { countryButton.setMaxLines(1); countryButton.setSingleLine(true); countryButton.setEllipsize(TextUtils.TruncateAt.END); - countryButton.setGravity(Gravity.LEFT | Gravity.CENTER_HORIZONTAL); + countryButton.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_HORIZONTAL); countryButton.setBackgroundResource(R.drawable.spinner_states); addView(countryButton); LayoutParams layoutParams = (LayoutParams) countryButton.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(36); layoutParams.bottomMargin = AndroidUtilities.dp(14); countryButton.setLayoutParams(layoutParams); @@ -452,7 +456,7 @@ public class LoginActivity extends BaseFragment { view.setBackgroundColor(0xffdbdbdb); addView(view); layoutParams = (LayoutParams) view.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = 1; layoutParams.topMargin = AndroidUtilities.dp(-17.5f); layoutParams.leftMargin = AndroidUtilities.dp(4); @@ -463,8 +467,8 @@ public class LoginActivity extends BaseFragment { linearLayout.setOrientation(HORIZONTAL); addView(linearLayout); layoutParams = (LayoutParams) linearLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.topMargin = AndroidUtilities.dp(20); linearLayout.setLayoutParams(layoutParams); @@ -474,8 +478,8 @@ public class LoginActivity extends BaseFragment { textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); linearLayout.addView(textView); layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; textView.setLayoutParams(layoutParams); codeField = new EditText(context); @@ -565,7 +569,7 @@ public class LoginActivity extends BaseFragment { phoneField.setImeOptions(EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NO_EXTRACT_UI); linearLayout.addView(phoneField); layoutParams = (LayoutParams) phoneField.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(36); phoneField.setLayoutParams(layoutParams); phoneField.addTextChangedListener(new TextWatcher() { @@ -630,15 +634,15 @@ public class LoginActivity extends BaseFragment { textView.setText(LocaleController.getString("ChangePhoneHelp", R.string.ChangePhoneHelp)); textView.setTextColor(0xff757575); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - textView.setGravity(Gravity.LEFT); + textView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); textView.setLineSpacing(AndroidUtilities.dp(2), 1.0f); addView(textView); layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.topMargin = AndroidUtilities.dp(28); layoutParams.bottomMargin = AndroidUtilities.dp(10); - layoutParams.gravity = Gravity.LEFT; + layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; textView.setLayoutParams(layoutParams); HashMap languageMap = new HashMap<>(); @@ -769,6 +773,7 @@ public class LoginActivity extends BaseFragment { needShowAlert(LocaleController.getString("AppName", R.string.AppName), LocaleController.getString("InvalidPhoneNumber", R.string.InvalidPhoneNumber)); return; } + ConnectionsManager.getInstance().cleanUp(); TLRPC.TL_auth_sendCode req = new TLRPC.TL_auth_sendCode(); String phone = PhoneFormat.stripExceptNumbers("" + codeField.getText() + phoneField.getText()); ConnectionsManager.getInstance().applyCountryPortNumber(phone); @@ -776,7 +781,7 @@ public class LoginActivity extends BaseFragment { req.api_id = BuildVars.APP_ID; req.sms_type = 0; req.phone_number = phone; - req.lang_code = LocaleController.getLocaleString(Locale.getDefault()); + req.lang_code = LocaleController.getLocaleString(LocaleController.getInstance().getSystemDefaultLocale()); if (req.lang_code == null || req.lang_code.length() == 0) { req.lang_code = "en"; } @@ -895,13 +900,13 @@ public class LoginActivity extends BaseFragment { confirmTextView = new TextView(context); confirmTextView.setTextColor(0xff757575); confirmTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - confirmTextView.setGravity(Gravity.LEFT); + confirmTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); confirmTextView.setLineSpacing(AndroidUtilities.dp(2), 1.0f); addView(confirmTextView); LayoutParams layoutParams = (LayoutParams) confirmTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.LEFT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; confirmTextView.setLayoutParams(layoutParams); codeField = new EditText(context); @@ -916,7 +921,7 @@ public class LoginActivity extends BaseFragment { codeField.setPadding(0, 0, 0, 0); addView(codeField); layoutParams = (LayoutParams) codeField.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(36); layoutParams.gravity = Gravity.CENTER_HORIZONTAL; layoutParams.topMargin = AndroidUtilities.dp(20); @@ -936,28 +941,28 @@ public class LoginActivity extends BaseFragment { timeText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); timeText.setTextColor(0xff757575); timeText.setLineSpacing(AndroidUtilities.dp(2), 1.0f); - timeText.setGravity(Gravity.LEFT); + timeText.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); addView(timeText); layoutParams = (LayoutParams) timeText.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.LEFT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; layoutParams.topMargin = AndroidUtilities.dp(30); timeText.setLayoutParams(layoutParams); problemText = new TextView(context); problemText.setText(LocaleController.getString("DidNotGetTheCode", R.string.DidNotGetTheCode)); problemText.setVisibility(time < 1000 ? VISIBLE : GONE); - problemText.setGravity(Gravity.LEFT); + problemText.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); problemText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); problemText.setTextColor(0xff4d83b3); problemText.setLineSpacing(AndroidUtilities.dp(2), 1.0f); problemText.setPadding(0, AndroidUtilities.dp(2), 0, AndroidUtilities.dp(12)); addView(problemText); layoutParams = (LayoutParams) problemText.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.LEFT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; layoutParams.topMargin = AndroidUtilities.dp(20); problemText.setLayoutParams(layoutParams); problemText.setOnClickListener(new OnClickListener() { @@ -980,24 +985,25 @@ public class LoginActivity extends BaseFragment { }); LinearLayout linearLayout = new LinearLayout(context); - linearLayout.setGravity(Gravity.BOTTOM | Gravity.CENTER_VERTICAL); + linearLayout.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); addView(linearLayout); layoutParams = (LayoutParams) linearLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; + layoutParams.gravity = (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); linearLayout.setLayoutParams(layoutParams); TextView wrongNumber = new TextView(context); - wrongNumber.setGravity(Gravity.LEFT | Gravity.CENTER_HORIZONTAL); + wrongNumber.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_HORIZONTAL); wrongNumber.setTextColor(0xff4d83b3); wrongNumber.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); wrongNumber.setLineSpacing(AndroidUtilities.dp(2), 1.0f); wrongNumber.setPadding(0, AndroidUtilities.dp(24), 0, 0); linearLayout.addView(wrongNumber); layoutParams = (LayoutParams) wrongNumber.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.BOTTOM | Gravity.LEFT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.gravity = Gravity.BOTTOM | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); layoutParams.bottomMargin = AndroidUtilities.dp(10); wrongNumber.setLayoutParams(layoutParams); wrongNumber.setText(LocaleController.getString("WrongNumber", R.string.WrongNumber)); @@ -1036,7 +1042,7 @@ public class LoginActivity extends BaseFragment { } String number = PhoneFormat.getInstance().format(phone); - String str = String.format(Locale.US, LocaleController.getString("SentSmsCode", R.string.SentSmsCode) + " %s", number); + String str = String.format(LocaleController.getString("SentSmsCode", R.string.SentSmsCode) + " %s", number); try { SpannableStringBuilder stringBuilder = new SpannableStringBuilder(str); TypefaceSpan span = new TypefaceSpan(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); @@ -1300,18 +1306,13 @@ public class LoginActivity extends BaseFragment { @Override public void didReceivedNotification(int id, final Object... args) { if (id == NotificationCenter.didReceiveSmsCode) { - AndroidUtilities.runOnUIThread(new Runnable() { - @Override - public void run() { - if (!waitingForSms) { - return; - } - if (codeField != null) { - codeField.setText("" + args[0]); - onNextPressed(); - } - } - }); + if (!waitingForSms) { + return; + } + if (codeField != null) { + codeField.setText("" + args[0]); + onNextPressed(); + } } } @@ -1371,14 +1372,14 @@ public class LoginActivity extends BaseFragment { confirmTextView = new TextView(context); confirmTextView.setTextColor(0xff757575); confirmTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - confirmTextView.setGravity(Gravity.LEFT); + confirmTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); confirmTextView.setLineSpacing(AndroidUtilities.dp(2), 1.0f); confirmTextView.setText(LocaleController.getString("LoginPasswordText", R.string.LoginPasswordText)); addView(confirmTextView); LayoutParams layoutParams = (LayoutParams) confirmTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.LEFT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; confirmTextView.setLayoutParams(layoutParams); codeField = new EditText(context); @@ -1393,9 +1394,10 @@ public class LoginActivity extends BaseFragment { codeField.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); codeField.setTransformationMethod(PasswordTransformationMethod.getInstance()); codeField.setTypeface(Typeface.DEFAULT); + codeField.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); addView(codeField); layoutParams = (LayoutParams) codeField.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(36); layoutParams.gravity = Gravity.CENTER_HORIZONTAL; layoutParams.topMargin = AndroidUtilities.dp(20); @@ -1412,7 +1414,7 @@ public class LoginActivity extends BaseFragment { }); TextView cancelButton = new TextView(context); - cancelButton.setGravity(Gravity.LEFT | Gravity.TOP); + cancelButton.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); cancelButton.setTextColor(0xff4d83b3); cancelButton.setText(LocaleController.getString("ForgotPassword", R.string.ForgotPassword)); cancelButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); @@ -1420,9 +1422,9 @@ public class LoginActivity extends BaseFragment { cancelButton.setPadding(0, AndroidUtilities.dp(14), 0, 0); addView(cancelButton); layoutParams = (LayoutParams) cancelButton.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.TOP | Gravity.LEFT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.gravity = Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); cancelButton.setLayoutParams(layoutParams); cancelButton.setOnClickListener(new OnClickListener() { @Override @@ -1450,7 +1452,7 @@ public class LoginActivity extends BaseFragment { setPage(4, true, bundle, false); } }); - AlertDialog dialog = showAlertDialog(builder); + Dialog dialog = showDialog(builder.create()); if (dialog != null) { dialog.setCanceledOnTouchOutside(false); dialog.setCancelable(false); @@ -1483,7 +1485,7 @@ public class LoginActivity extends BaseFragment { }); resetAccountButton = new TextView(context); - resetAccountButton.setGravity(Gravity.LEFT | Gravity.TOP); + resetAccountButton.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); resetAccountButton.setTextColor(0xffff6666); resetAccountButton.setVisibility(GONE); resetAccountButton.setText(LocaleController.getString("ResetMyAccount", R.string.ResetMyAccount)); @@ -1493,9 +1495,9 @@ public class LoginActivity extends BaseFragment { resetAccountButton.setPadding(0, AndroidUtilities.dp(14), 0, 0); addView(resetAccountButton); layoutParams = (LayoutParams) resetAccountButton.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.TOP | Gravity.LEFT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.gravity = Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); layoutParams.topMargin = AndroidUtilities.dp(34); resetAccountButton.setLayoutParams(layoutParams); resetAccountButton.setOnClickListener(new OnClickListener() { @@ -1533,12 +1535,12 @@ public class LoginActivity extends BaseFragment { } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } }); resetAccountText = new TextView(context); - resetAccountText.setGravity(Gravity.LEFT | Gravity.TOP); + resetAccountText.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); resetAccountText.setVisibility(GONE); resetAccountText.setTextColor(0xff757575); resetAccountText.setText(LocaleController.getString("ResetMyAccountText", R.string.ResetMyAccountText)); @@ -1546,9 +1548,9 @@ public class LoginActivity extends BaseFragment { resetAccountText.setLineSpacing(AndroidUtilities.dp(2), 1.0f); addView(resetAccountText); layoutParams = (LayoutParams) resetAccountText.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.TOP | Gravity.LEFT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.gravity = Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); layoutParams.bottomMargin = AndroidUtilities.dp(14); layoutParams.topMargin = AndroidUtilities.dp(7); resetAccountText.setLayoutParams(layoutParams); @@ -1746,14 +1748,14 @@ public class LoginActivity extends BaseFragment { confirmTextView = new TextView(context); confirmTextView.setTextColor(0xff757575); confirmTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - confirmTextView.setGravity(Gravity.LEFT); + confirmTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT)); confirmTextView.setLineSpacing(AndroidUtilities.dp(2), 1.0f); confirmTextView.setText(LocaleController.getString("RestoreEmailSentInfo", R.string.RestoreEmailSentInfo)); addView(confirmTextView); LayoutParams layoutParams = (LayoutParams) confirmTextView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.LEFT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.gravity = (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); confirmTextView.setLayoutParams(layoutParams); codeField = new EditText(context); @@ -1768,9 +1770,10 @@ public class LoginActivity extends BaseFragment { codeField.setInputType(InputType.TYPE_CLASS_PHONE); codeField.setTransformationMethod(PasswordTransformationMethod.getInstance()); codeField.setTypeface(Typeface.DEFAULT); + codeField.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); addView(codeField); layoutParams = (LayoutParams) codeField.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(36); layoutParams.gravity = Gravity.CENTER_HORIZONTAL; layoutParams.topMargin = AndroidUtilities.dp(20); @@ -1787,16 +1790,16 @@ public class LoginActivity extends BaseFragment { }); cancelButton = new TextView(context); - cancelButton.setGravity(Gravity.LEFT | Gravity.BOTTOM); + cancelButton.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.BOTTOM); cancelButton.setTextColor(0xff4d83b3); cancelButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); cancelButton.setLineSpacing(AndroidUtilities.dp(2), 1.0f); cancelButton.setPadding(0, AndroidUtilities.dp(14), 0, 0); addView(cancelButton); layoutParams = (LayoutParams) cancelButton.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.BOTTOM | Gravity.LEFT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.gravity = Gravity.BOTTOM | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); layoutParams.bottomMargin = AndroidUtilities.dp(14); cancelButton.setLayoutParams(layoutParams); cancelButton.setOnClickListener(new OnClickListener() { @@ -1811,7 +1814,7 @@ public class LoginActivity extends BaseFragment { setPage(3, true, new Bundle(), true); } }); - AlertDialog dialog = showAlertDialog(builder); + Dialog dialog = showDialog(builder.create()); if (dialog != null) { dialog.setCanceledOnTouchOutside(false); dialog.setCancelable(false); @@ -1870,12 +1873,6 @@ public class LoginActivity extends BaseFragment { return; } nextPressed = true; - byte[] oldPasswordBytes = null; - try { - oldPasswordBytes = oldPassword.getBytes("UTF-8"); - } catch (Exception e) { - FileLog.e("tmessages", e); - } String code = codeField.getText().toString(); if (code.length() == 0) { @@ -1992,14 +1989,14 @@ public class LoginActivity extends BaseFragment { TextView textView = new TextView(context); textView.setText(LocaleController.getString("RegisterText", R.string.RegisterText)); textView.setTextColor(0xff757575); - textView.setGravity(Gravity.LEFT); + textView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); addView(textView); LayoutParams layoutParams = (LayoutParams) textView.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.topMargin = AndroidUtilities.dp(8); - layoutParams.gravity = Gravity.LEFT; + layoutParams.gravity = LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT; textView.setLayoutParams(layoutParams); firstNameField = new EditText(context); @@ -2013,7 +2010,7 @@ public class LoginActivity extends BaseFragment { firstNameField.setInputType(InputType.TYPE_TEXT_FLAG_CAP_WORDS); addView(firstNameField); layoutParams = (LayoutParams) firstNameField.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(36); layoutParams.topMargin = AndroidUtilities.dp(26); firstNameField.setLayoutParams(layoutParams); @@ -2039,7 +2036,7 @@ public class LoginActivity extends BaseFragment { lastNameField.setInputType(InputType.TYPE_TEXT_FLAG_CAP_WORDS); addView(lastNameField); layoutParams = (LayoutParams) lastNameField.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(36); layoutParams.topMargin = AndroidUtilities.dp(10); lastNameField.setLayoutParams(layoutParams); @@ -2048,22 +2045,22 @@ public class LoginActivity extends BaseFragment { linearLayout.setGravity(Gravity.BOTTOM | Gravity.CENTER_VERTICAL); addView(linearLayout); layoutParams = (LayoutParams) linearLayout.getLayoutParams(); - layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; linearLayout.setLayoutParams(layoutParams); TextView wrongNumber = new TextView(context); wrongNumber.setText(LocaleController.getString("CancelRegistration", R.string.CancelRegistration)); - wrongNumber.setGravity(Gravity.LEFT | Gravity.CENTER_HORIZONTAL); + wrongNumber.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_HORIZONTAL); wrongNumber.setTextColor(0xff4d83b3); wrongNumber.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); wrongNumber.setLineSpacing(AndroidUtilities.dp(2), 1.0f); wrongNumber.setPadding(0, AndroidUtilities.dp(24), 0, 0); linearLayout.addView(wrongNumber); layoutParams = (LayoutParams) wrongNumber.getLayoutParams(); - layoutParams.width = LayoutParams.WRAP_CONTENT; - layoutParams.height = LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.BOTTOM | Gravity.LEFT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.gravity = Gravity.BOTTOM | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); layoutParams.bottomMargin = AndroidUtilities.dp(10); wrongNumber.setLayoutParams(layoutParams); wrongNumber.setOnClickListener(new OnClickListener() { @@ -2080,7 +2077,7 @@ public class LoginActivity extends BaseFragment { } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } }); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/MediaActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/MediaActivity.java index 90d6f84fb..cc14a0e98 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/MediaActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/MediaActivity.java @@ -59,8 +59,8 @@ import org.telegram.ui.ActionBar.ActionBarPopupWindow; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.Adapters.BaseFragmentAdapter; import org.telegram.ui.Adapters.BaseSectionsAdapter; -import org.telegram.ui.AnimationCompat.AnimatorSetProxy; -import org.telegram.ui.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.AnimatorSetProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; import org.telegram.ui.Cells.GreySectionCell; import org.telegram.ui.Cells.LoadingCell; import org.telegram.ui.Cells.SharedDocumentCell; @@ -68,6 +68,7 @@ import org.telegram.ui.Cells.SharedMediaSectionCell; import org.telegram.ui.Cells.SharedPhotoVideoCell; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.SectionsListView; import java.io.File; @@ -278,7 +279,7 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } else if (id == forward) { Bundle args = new Bundle(); args.putBoolean("onlySelect", true); @@ -377,8 +378,8 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No dropDownContainer.addSubItem(files_item, LocaleController.getString("DocumentsTitle", R.string.DocumentsTitle), 0); actionBar.addView(dropDownContainer); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) dropDownContainer.getLayoutParams(); - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; layoutParams.rightMargin = AndroidUtilities.dp(40); layoutParams.leftMargin = AndroidUtilities.isTablet() ? AndroidUtilities.dp(64) : AndroidUtilities.dp(56); layoutParams.gravity = Gravity.TOP | Gravity.LEFT; @@ -403,8 +404,8 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No dropDown.setPadding(0, 0, AndroidUtilities.dp(10), 0); dropDownContainer.addView(dropDown); layoutParams = (FrameLayout.LayoutParams) dropDown.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(16); layoutParams.gravity = Gravity.CENTER_VERTICAL; dropDown.setLayoutParams(layoutParams); @@ -431,7 +432,7 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No LinearLayout.LayoutParams layoutParams1 = (LinearLayout.LayoutParams) selectedMessagesCountTextView.getLayoutParams(); layoutParams1.weight = 1; layoutParams1.width = 0; - layoutParams1.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams1.height = LayoutHelper.MATCH_PARENT; selectedMessagesCountTextView.setLayoutParams(layoutParams1); if ((int) dialog_id != 0) { @@ -453,8 +454,8 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No listView.setClipToPadding(false); frameLayout.addView(listView); layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP; listView.setLayoutParams(layoutParams); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @@ -518,8 +519,8 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No emptyView.setBackgroundColor(0xfff0f0f0); frameLayout.addView(emptyView); layoutParams = (FrameLayout.LayoutParams) emptyView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; emptyView.setLayoutParams(layoutParams); emptyView.setOnTouchListener(new View.OnTouchListener() { @Override @@ -531,8 +532,8 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No emptyImageView = new ImageView(context); emptyView.addView(emptyImageView); layoutParams1 = (LinearLayout.LayoutParams) emptyImageView.getLayoutParams(); - layoutParams1.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams1.height = LinearLayout.LayoutParams.WRAP_CONTENT; + layoutParams1.width = LayoutHelper.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; emptyImageView.setLayoutParams(layoutParams1); emptyTextView = new TextView(context); @@ -543,8 +544,8 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No emptyView.addView(emptyTextView); layoutParams1 = (LinearLayout.LayoutParams) emptyTextView.getLayoutParams(); layoutParams1.topMargin = AndroidUtilities.dp(24); - layoutParams1.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams1.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams1.width = LayoutHelper.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; layoutParams1.gravity = Gravity.CENTER; emptyTextView.setLayoutParams(layoutParams1); @@ -555,15 +556,15 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No progressView.setBackgroundColor(0xfff0f0f0); frameLayout.addView(progressView); layoutParams = (FrameLayout.LayoutParams) progressView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; progressView.setLayoutParams(layoutParams); ProgressBar progressBar = new ProgressBar(context); progressView.addView(progressBar); layoutParams1 = (LinearLayout.LayoutParams) progressBar.getLayoutParams(); - layoutParams1.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams1.height = LinearLayout.LayoutParams.WRAP_CONTENT; + layoutParams1.width = LayoutHelper.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; progressBar.setLayoutParams(layoutParams1); switchToCurrentSelectedMode(); @@ -937,7 +938,7 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null); builder.setMessage(LocaleController.formatString("NoHandleAppInstalled", R.string.NoHandleAppInstalled, message.messageOwner.media.document.mime_type)); - showAlertDialog(builder); + showDialog(builder.create()); } } } else if (!cell.isLoading()) { @@ -1139,7 +1140,7 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No MessageObject messageObject = messageObjects.get(0); ((SharedMediaSectionCell) convertView).setText(LocaleController.formatterMonthYear.format((long) messageObject.messageOwner.date * 1000).toUpperCase()); } else { - SharedPhotoVideoCell cell = null; + SharedPhotoVideoCell cell; if (convertView == null) { if (!cellCache.isEmpty()) { convertView = cellCache.get(0); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/MessagesActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/MessagesActivity.java index 4d56274f6..ed15ce845 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/MessagesActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/MessagesActivity.java @@ -10,6 +10,7 @@ package org.telegram.ui; import android.animation.ObjectAnimator; import android.animation.StateListAnimator; +import android.annotation.SuppressLint; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; @@ -17,26 +18,27 @@ import android.content.res.Configuration; import android.graphics.Outline; import android.os.Build; import android.os.Bundle; +import android.util.TypedValue; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; -import android.view.ViewGroup; import android.view.ViewOutlineProvider; import android.view.ViewTreeObserver; import android.view.animation.AccelerateDecelerateInterpolator; -import android.widget.AbsListView; -import android.widget.AdapterView; -import android.widget.CheckBox; import android.widget.EditText; import android.widget.FrameLayout; import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.ListView; +import android.widget.ProgressBar; import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.LocaleController; import org.telegram.android.MessageObject; +import org.telegram.android.support.widget.LinearLayoutManager; +import org.telegram.android.support.widget.RecyclerView; import org.telegram.messenger.FileLog; import org.telegram.messenger.TLRPC; import org.telegram.android.ContactsController; @@ -45,11 +47,11 @@ import org.telegram.android.MessagesStorage; import org.telegram.android.NotificationCenter; import org.telegram.messenger.R; import org.telegram.messenger.UserConfig; -import org.telegram.ui.Adapters.BaseFragmentAdapter; +import org.telegram.ui.ActionBar.BottomSheet; import org.telegram.ui.Adapters.DialogsAdapter; import org.telegram.ui.Adapters.DialogsSearchAdapter; -import org.telegram.ui.AnimationCompat.ObjectAnimatorProxy; -import org.telegram.ui.AnimationCompat.ViewProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.ViewProxy; import org.telegram.ui.Cells.UserCell; import org.telegram.ui.Cells.DialogCell; import org.telegram.ui.ActionBar.ActionBar; @@ -57,18 +59,25 @@ import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.MenuDrawable; +import org.telegram.ui.Components.EmptyTextProgressView; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.ResourceLoader; import java.util.ArrayList; public class MessagesActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { - private ListView messagesListView; + + private RecyclerListView listView; + private LinearLayoutManager layoutManager; private DialogsAdapter dialogsAdapter; private DialogsSearchAdapter dialogsSearchAdapter; - private View searchEmptyView; - private View progressView; - private View emptyView; + private EmptyTextProgressView searchEmptyView; + private ProgressBar progressView; + private LinearLayout emptyView; private ActionBarMenuItem passcodeItem; private ImageView floatingButton; + private int prevPosition; private int prevTop; private boolean scrollUpdated; @@ -77,21 +86,18 @@ public class MessagesActivity extends BaseFragment implements NotificationCenter private String selectAlertString; private String selectAlertStringGroup; - private boolean serverOnly = false; + private boolean serverOnly; - private static boolean dialogsLoaded = false; - private boolean searching = false; - private boolean searchWas = false; - private boolean onlySelect = false; + private static boolean dialogsLoaded; + private boolean searching; + private boolean searchWas; + private boolean onlySelect; private long selectedDialog; private String searchString; + private long openedDialogId; private MessagesActivityDelegate delegate; - private long openedDialogId = 0; - - private static final int passcode_menu_item = 1; - public interface MessagesActivityDelegate { void didSelectDialog(MessagesActivity fragment, long dialog_id, boolean param); } @@ -156,28 +162,27 @@ public class MessagesActivity extends BaseFragment implements NotificationCenter } @Override - public View createView(Context context, LayoutInflater inflater) { + public View createView(final Context context, LayoutInflater inflater) { searching = false; searchWas = false; + ResourceLoader.loadRecources(context); + ActionBarMenu menu = actionBar.createMenu(); if (!onlySelect && searchString == null) { - passcodeItem = menu.addItem(passcode_menu_item, R.drawable.lock_close); + passcodeItem = menu.addItem(1, R.drawable.lock_close); updatePasscodeButton(); } ActionBarMenuItem item = menu.addItem(0, R.drawable.ic_ab_search).setIsSearchField(true).setActionBarMenuItemSearchListener(new ActionBarMenuItem.ActionBarMenuItemSearchListener() { @Override public void onSearchExpand() { searching = true; - if (messagesListView != null) { + if (listView != null) { if (searchString != null) { - messagesListView.setEmptyView(progressView); - searchEmptyView.setVisibility(View.INVISIBLE); - } else { - messagesListView.setEmptyView(searchEmptyView); + listView.setEmptyView(searchEmptyView); progressView.setVisibility(View.INVISIBLE); + emptyView.setVisibility(View.INVISIBLE); } - emptyView.setVisibility(View.INVISIBLE); if (!onlySelect) { floatingButton.setVisibility(View.GONE); } @@ -193,16 +198,14 @@ public class MessagesActivity extends BaseFragment implements NotificationCenter } searching = false; searchWas = false; - if (messagesListView != null) { + if (listView != null) { + searchEmptyView.setVisibility(View.INVISIBLE); if (MessagesController.getInstance().loadingDialogs && MessagesController.getInstance().dialogs.isEmpty()) { - searchEmptyView.setVisibility(View.INVISIBLE); emptyView.setVisibility(View.INVISIBLE); - progressView.setVisibility(View.VISIBLE); - messagesListView.setEmptyView(progressView); + listView.setEmptyView(progressView); } else { - messagesListView.setEmptyView(emptyView); - searchEmptyView.setVisibility(View.INVISIBLE); progressView.setVisibility(View.INVISIBLE); + listView.setEmptyView(emptyView); } if (!onlySelect) { floatingButton.setVisibility(View.VISIBLE); @@ -210,8 +213,8 @@ public class MessagesActivity extends BaseFragment implements NotificationCenter ViewProxy.setTranslationY(floatingButton, AndroidUtilities.dp(100)); hideFloatingButton(false); } - if (messagesListView.getAdapter() != dialogsAdapter) { - messagesListView.setAdapter(dialogsAdapter); + if (listView.getAdapter() != dialogsAdapter) { + listView.setAdapter(dialogsAdapter); dialogsAdapter.notifyDataSetChanged(); } } @@ -228,13 +231,14 @@ public class MessagesActivity extends BaseFragment implements NotificationCenter if (text.length() != 0) { searchWas = true; if (dialogsSearchAdapter != null) { - messagesListView.setAdapter(dialogsSearchAdapter); + listView.setAdapter(dialogsSearchAdapter); dialogsSearchAdapter.notifyDataSetChanged(); } - if (searchEmptyView != null && messagesListView.getEmptyView() == emptyView) { - messagesListView.setEmptyView(searchEmptyView); + if (searchEmptyView != null && listView.getEmptyView() != searchEmptyView) { emptyView.setVisibility(View.INVISIBLE); progressView.setVisibility(View.INVISIBLE); + searchEmptyView.showTextView(); + listView.setEmptyView(searchEmptyView); } } if (dialogsSearchAdapter != null) { @@ -265,7 +269,7 @@ public class MessagesActivity extends BaseFragment implements NotificationCenter } else if (parentLayout != null) { parentLayout.getDrawerLayoutContainer().openDrawer(false); } - } else if (id == passcode_menu_item) { + } else if (id == 1) { UserConfig.appLocked = !UserConfig.appLocked; UserConfig.saveConfig(false); updatePasscodeButton(); @@ -273,127 +277,47 @@ public class MessagesActivity extends BaseFragment implements NotificationCenter } }); - fragmentView = inflater.inflate(R.layout.messages_list, null, false); - if (searchString == null) { - dialogsAdapter = new DialogsAdapter(context, serverOnly); - if (AndroidUtilities.isTablet() && openedDialogId != 0) { - dialogsAdapter.setOpenedDialogId(openedDialogId); - } - } - int type = 0; - if (searchString != null) { - type = 2; - } else if (!onlySelect) { - type = 1; - } - dialogsSearchAdapter = new DialogsSearchAdapter(context, type); - dialogsSearchAdapter.setDelegate(new DialogsSearchAdapter.MessagesActivitySearchAdapterDelegate() { + FrameLayout frameLayout = new FrameLayout(context); + fragmentView = frameLayout; + + listView = new RecyclerListView(context); + listView.setVerticalScrollBarEnabled(true); + listView.setItemAnimator(null); + listView.setInstantClick(true); + listView.setLayoutAnimation(null); + layoutManager = new LinearLayoutManager(context) { @Override - public void searchStateChanged(boolean search) { - if (searching && searchWas && messagesListView != null) { - progressView.setVisibility(search ? View.VISIBLE : View.INVISIBLE); - searchEmptyView.setVisibility(search ? View.INVISIBLE : View.VISIBLE); - messagesListView.setEmptyView(search ? progressView : searchEmptyView); - } + public boolean supportsPredictiveItemAnimations() { + return false; } - }); - - messagesListView = (ListView) fragmentView.findViewById(R.id.messages_list_view); - if (dialogsAdapter != null) { - messagesListView.setAdapter(dialogsAdapter); - } + }; + layoutManager.setOrientation(LinearLayoutManager.VERTICAL); + listView.setLayoutManager(layoutManager); if (Build.VERSION.SDK_INT >= 11) { - messagesListView.setVerticalScrollbarPosition(LocaleController.isRTL ? ListView.SCROLLBAR_POSITION_LEFT : ListView.SCROLLBAR_POSITION_RIGHT); + listView.setVerticalScrollbarPosition(LocaleController.isRTL ? ListView.SCROLLBAR_POSITION_LEFT : ListView.SCROLLBAR_POSITION_RIGHT); } - - progressView = fragmentView.findViewById(R.id.progressLayout); - searchEmptyView = fragmentView.findViewById(R.id.search_empty_view); - searchEmptyView.setOnTouchListener(new View.OnTouchListener() { + frameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + listView.setOnItemClickListener(new RecyclerListView.OnItemClickListener() { @Override - public boolean onTouch(View v, MotionEvent event) { - return true; - } - }); - emptyView = fragmentView.findViewById(R.id.list_empty_view); - emptyView.setOnTouchListener(new View.OnTouchListener() { - @Override - public boolean onTouch(View v, MotionEvent event) { - return true; - } - }); - - - TextView textView = (TextView) fragmentView.findViewById(R.id.list_empty_view_text1); - textView.setText(LocaleController.getString("NoChats", R.string.NoChats)); - textView = (TextView) fragmentView.findViewById(R.id.list_empty_view_text2); - String help = LocaleController.getString("NoChatsHelp", R.string.NoChatsHelp); - if (AndroidUtilities.isTablet() && !AndroidUtilities.isSmallTablet()) { - help = help.replace("\n", " "); - } - textView.setText(help); - textView = (TextView) fragmentView.findViewById(R.id.search_empty_text); - textView.setText(LocaleController.getString("NoResult", R.string.NoResult)); - - floatingButton = (ImageView) fragmentView.findViewById(R.id.floating_button); - floatingButton.setVisibility(onlySelect ? View.GONE : View.VISIBLE); - floatingButton.setScaleType(ImageView.ScaleType.CENTER); - if (Build.VERSION.SDK_INT >= 21) { - StateListAnimator animator = new StateListAnimator(); - animator.addState(new int[]{android.R.attr.state_pressed}, ObjectAnimator.ofFloat(floatingButton, "translationZ", AndroidUtilities.dp(2), AndroidUtilities.dp(4)).setDuration(200)); - animator.addState(new int[]{}, ObjectAnimator.ofFloat(floatingButton, "translationZ", AndroidUtilities.dp(4), AndroidUtilities.dp(2)).setDuration(200)); - floatingButton.setStateListAnimator(animator); - floatingButton.setOutlineProvider(new ViewOutlineProvider() { - @Override - public void getOutline(View view, Outline outline) { - outline.setOval(0, 0, AndroidUtilities.dp(56), AndroidUtilities.dp(56)); - } - }); - } - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) floatingButton.getLayoutParams(); - layoutParams.leftMargin = LocaleController.isRTL ? AndroidUtilities.dp(14) : 0; - layoutParams.rightMargin = LocaleController.isRTL ? 0 : AndroidUtilities.dp(14); - layoutParams.gravity = (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.BOTTOM; - floatingButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Bundle args = new Bundle(); - args.putBoolean("destroyAfterSelect", true); - presentFragment(new ContactsActivity(args)); - } - }); - - if (MessagesController.getInstance().loadingDialogs && MessagesController.getInstance().dialogs.isEmpty()) { - searchEmptyView.setVisibility(View.INVISIBLE); - emptyView.setVisibility(View.INVISIBLE); - progressView.setVisibility(View.VISIBLE); - messagesListView.setEmptyView(progressView); - } else { - messagesListView.setEmptyView(emptyView); - searchEmptyView.setVisibility(View.INVISIBLE); - progressView.setVisibility(View.INVISIBLE); - } - - messagesListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView adapterView, View view, int i, long l) { - if (messagesListView == null || messagesListView.getAdapter() == null) { + public void onItemClick(View view, int position) { + if (listView == null || listView.getAdapter() == null) { return; } long dialog_id = 0; int message_id = 0; - BaseFragmentAdapter adapter = (BaseFragmentAdapter) messagesListView.getAdapter(); + RecyclerView.Adapter adapter = listView.getAdapter(); if (adapter == dialogsAdapter) { - TLRPC.TL_dialog dialog = dialogsAdapter.getItem(i); + TLRPC.TL_dialog dialog = dialogsAdapter.getItem(position); if (dialog == null) { return; } dialog_id = dialog.id; } else if (adapter == dialogsSearchAdapter) { - Object obj = dialogsSearchAdapter.getItem(i); + Object obj = dialogsSearchAdapter.getItem(position); if (obj instanceof TLRPC.User) { dialog_id = ((TLRPC.User) obj).id; - if (dialogsSearchAdapter.isGlobalSearch(i)) { + if (dialogsSearchAdapter.isGlobalSearch(position)) { ArrayList users = new ArrayList<>(); users.add((TLRPC.User) obj); MessagesController.getInstance().putUsers(users, false); @@ -465,15 +389,14 @@ public class MessagesActivity extends BaseFragment implements NotificationCenter } } }); - - messagesListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { + listView.setOnItemLongClickListener(new RecyclerListView.OnItemLongClickListener() { @Override - public boolean onItemLongClick(AdapterView adapterView, View view, int i, long l) { + public void onItemClick(View view, int position) { if (onlySelect || searching && searchWas || getParentActivity() == null) { if (searchWas && searching) { - BaseFragmentAdapter adapter = (BaseFragmentAdapter) messagesListView.getAdapter(); + RecyclerView.Adapter adapter = listView.getAdapter(); if (adapter == dialogsSearchAdapter) { - Object item = adapter.getItem(i); + Object item = dialogsSearchAdapter.getItem(position); if (item instanceof String) { AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); @@ -485,30 +408,35 @@ public class MessagesActivity extends BaseFragment implements NotificationCenter } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); - return true; + showDialog(builder.create()); + return; } } } - return false; + return; } TLRPC.TL_dialog dialog; if (serverOnly) { - if (i >= MessagesController.getInstance().dialogsServerOnly.size()) { - return false; + if (position < 0 || position >= MessagesController.getInstance().dialogsServerOnly.size()) { + return; } - dialog = MessagesController.getInstance().dialogsServerOnly.get(i); + dialog = MessagesController.getInstance().dialogsServerOnly.get(position); } else { - if (i >= MessagesController.getInstance().dialogs.size()) { - return false; + if (position < 0 || position >= MessagesController.getInstance().dialogs.size()) { + return; } - dialog = MessagesController.getInstance().dialogs.get(i); + dialog = MessagesController.getInstance().dialogs.get(position); } selectedDialog = dialog.id; - AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + /*AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); + + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + showDialog(builder.create());*/ + + BottomSheet.Builder builder = new BottomSheet.Builder(getParentActivity()); int lower_id = (int) selectedDialog; int high_id = (int) (selectedDialog >> 32); @@ -533,7 +461,12 @@ public class MessagesActivity extends BaseFragment implements NotificationCenter public void onClick(DialogInterface dialogInterface, int i) { if (which != 0) { if (isChat) { - MessagesController.getInstance().deleteUserFromChat((int) -selectedDialog, MessagesController.getInstance().getUser(UserConfig.getClientUserId()), null); + TLRPC.Chat currentChat = MessagesController.getInstance().getChat((int) -selectedDialog); + if (currentChat != null && currentChat.left || currentChat instanceof TLRPC.TL_chatForbidden) { + MessagesController.getInstance().deleteDialog(selectedDialog, 0, false); + } else { + MessagesController.getInstance().deleteUserFromChat((int) -selectedDialog, MessagesController.getInstance().getUser(UserConfig.getClientUserId()), null); + } } else { MessagesController.getInstance().deleteDialog(selectedDialog, 0, false); } @@ -546,39 +479,111 @@ public class MessagesActivity extends BaseFragment implements NotificationCenter } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } }); - builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); + } + }); + + searchEmptyView = new EmptyTextProgressView(context); + searchEmptyView.setVisibility(View.INVISIBLE); + searchEmptyView.setShowAtCenter(true); + searchEmptyView.setText(LocaleController.getString("NoResult", R.string.NoResult)); + frameLayout.addView(searchEmptyView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + + emptyView = new LinearLayout(context); + emptyView.setOrientation(LinearLayout.VERTICAL); + emptyView.setVisibility(View.INVISIBLE); + emptyView.setGravity(Gravity.CENTER); + frameLayout.addView(emptyView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + emptyView.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { return true; } }); - messagesListView.setOnScrollListener(new AbsListView.OnScrollListener() { + TextView textView = new TextView(context); + textView.setText(LocaleController.getString("NoChats", R.string.NoChats)); + textView.setTextColor(0xff959595); + textView.setGravity(Gravity.CENTER); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); + emptyView.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); + + textView = new TextView(context); + String help = LocaleController.getString("NoChatsHelp", R.string.NoChatsHelp); + if (AndroidUtilities.isTablet() && !AndroidUtilities.isSmallTablet()) { + help = help.replace("\n", " "); + } + textView.setText(help); + textView.setTextColor(0xff959595); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); + textView.setGravity(Gravity.CENTER); + textView.setPadding(AndroidUtilities.dp(8), AndroidUtilities.dp(6), AndroidUtilities.dp(8), 0); + textView.setLineSpacing(AndroidUtilities.dp(2), 1); + emptyView.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); + + progressView = new ProgressBar(context); + progressView.setVisibility(View.INVISIBLE); + frameLayout.addView(progressView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); + + floatingButton = new ImageView(context); + floatingButton.setVisibility(onlySelect ? View.GONE : View.VISIBLE); + floatingButton.setScaleType(ImageView.ScaleType.CENTER); + floatingButton.setBackgroundResource(R.drawable.floating_states); + floatingButton.setImageResource(R.drawable.floating_pencil); + if (Build.VERSION.SDK_INT >= 21) { + StateListAnimator animator = new StateListAnimator(); + animator.addState(new int[]{android.R.attr.state_pressed}, ObjectAnimator.ofFloat(floatingButton, "translationZ", AndroidUtilities.dp(2), AndroidUtilities.dp(4)).setDuration(200)); + animator.addState(new int[]{}, ObjectAnimator.ofFloat(floatingButton, "translationZ", AndroidUtilities.dp(4), AndroidUtilities.dp(2)).setDuration(200)); + floatingButton.setStateListAnimator(animator); + floatingButton.setOutlineProvider(new ViewOutlineProvider() { + @SuppressLint("NewApi") + @Override + public void getOutline(View view, Outline outline) { + outline.setOval(0, 0, AndroidUtilities.dp(56), AndroidUtilities.dp(56)); + } + }); + } + frameLayout.addView(floatingButton, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.BOTTOM, LocaleController.isRTL ? 14 : 0, 0, LocaleController.isRTL ? 0 : 14, 14)); + floatingButton.setOnClickListener(new View.OnClickListener() { @Override - public void onScrollStateChanged(AbsListView absListView, int i) { - if (i == SCROLL_STATE_TOUCH_SCROLL && searching && searchWas) { + public void onClick(View v) { + Bundle args = new Bundle(); + args.putBoolean("destroyAfterSelect", true); + presentFragment(new ContactsActivity(args)); + } + }); + + listView.setOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + if (newState == RecyclerView.SCROLL_STATE_DRAGGING && searching && searchWas) { AndroidUtilities.hideKeyboard(getParentActivity().getCurrentFocus()); } } @Override - public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + int firstVisibleItem = layoutManager.findFirstVisibleItemPosition(); + int visibleItemCount = Math.abs(layoutManager.findLastVisibleItemPosition() - firstVisibleItem) + 1; + int totalItemCount = recyclerView.getAdapter().getItemCount(); + if (searching && searchWas) { - if (visibleItemCount > 0 && absListView.getLastVisiblePosition() == totalItemCount - 1 && !dialogsSearchAdapter.isMessagesSearchEndReached()) { + if (visibleItemCount > 0 && layoutManager.findLastVisibleItemPosition() == totalItemCount - 1 && !dialogsSearchAdapter.isMessagesSearchEndReached()) { dialogsSearchAdapter.loadMoreSearchMessages(); } return; } if (visibleItemCount > 0) { - if (absListView.getLastVisiblePosition() == MessagesController.getInstance().dialogs.size() && !serverOnly || absListView.getLastVisiblePosition() == MessagesController.getInstance().dialogsServerOnly.size() && serverOnly) { + if (layoutManager.findLastVisibleItemPosition() == MessagesController.getInstance().dialogs.size() && !serverOnly || layoutManager.findLastVisibleItemPosition() == MessagesController.getInstance().dialogsServerOnly.size() && serverOnly) { MessagesController.getInstance().loadDialogs(MessagesController.getInstance().dialogs.size(), MessagesController.getInstance().dialogsServerOnly.size(), 100, true); } } if (floatingButton.getVisibility() != View.GONE) { - final View topChild = absListView.getChildAt(0); + final View topChild = recyclerView.getChildAt(0); int firstViewTop = 0; if (topChild != null) { firstViewTop = topChild.getTop(); @@ -602,6 +607,42 @@ public class MessagesActivity extends BaseFragment implements NotificationCenter } }); + if (searchString == null) { + dialogsAdapter = new DialogsAdapter(context, serverOnly); + if (AndroidUtilities.isTablet() && openedDialogId != 0) { + dialogsAdapter.setOpenedDialogId(openedDialogId); + } + listView.setAdapter(dialogsAdapter); + } + int type = 0; + if (searchString != null) { + type = 2; + } else if (!onlySelect) { + type = 1; + } + dialogsSearchAdapter = new DialogsSearchAdapter(context, type); + dialogsSearchAdapter.setDelegate(new DialogsSearchAdapter.MessagesActivitySearchAdapterDelegate() { + @Override + public void searchStateChanged(boolean search) { + if (searching && searchWas && searchEmptyView != null) { + if (search) { + searchEmptyView.showProgress(); + } else { + searchEmptyView.showTextView(); + } + } + } + }); + + if (MessagesController.getInstance().loadingDialogs && MessagesController.getInstance().dialogs.isEmpty()) { + searchEmptyView.setVisibility(View.INVISIBLE); + emptyView.setVisibility(View.INVISIBLE); + listView.setEmptyView(progressView); + } else { + searchEmptyView.setVisibility(View.INVISIBLE); + progressView.setVisibility(View.INVISIBLE); + listView.setEmptyView(emptyView); + } if (searchString != null) { actionBar.openSearchField(searchString); } @@ -655,32 +696,32 @@ public class MessagesActivity extends BaseFragment implements NotificationCenter if (dialogsSearchAdapter != null) { dialogsSearchAdapter.notifyDataSetChanged(); } - if (messagesListView != null) { + if (listView != null) { try { if (MessagesController.getInstance().loadingDialogs && MessagesController.getInstance().dialogs.isEmpty()) { searchEmptyView.setVisibility(View.INVISIBLE); emptyView.setVisibility(View.INVISIBLE); - messagesListView.setEmptyView(progressView); + listView.setEmptyView(progressView); } else { - if (searching && searchWas) { - messagesListView.setEmptyView(searchEmptyView); - emptyView.setVisibility(View.INVISIBLE); - } else { - messagesListView.setEmptyView(emptyView); - searchEmptyView.setVisibility(View.INVISIBLE); - } progressView.setVisibility(View.INVISIBLE); + if (searching && searchWas) { + emptyView.setVisibility(View.INVISIBLE); + listView.setEmptyView(searchEmptyView); + } else { + searchEmptyView.setVisibility(View.INVISIBLE); + listView.setEmptyView(emptyView); + } } } catch (Exception e) { FileLog.e("tmessages", e); //TODO fix it in other way? } } } else if (id == NotificationCenter.emojiDidLoaded) { - if (messagesListView != null) { + if (listView != null) { updateVisibleRows(0); } } else if (id == NotificationCenter.updateInterfaces) { - updateVisibleRows((Integer)args[0]); + updateVisibleRows((Integer) args[0]); } else if (id == NotificationCenter.appDidLogout) { dialogsLoaded = false; } else if (id == NotificationCenter.encryptedChatUpdated) { @@ -689,8 +730,8 @@ public class MessagesActivity extends BaseFragment implements NotificationCenter updateVisibleRows(0); } else if (id == NotificationCenter.openedChatChanged) { if (!serverOnly && AndroidUtilities.isTablet()) { - boolean close = (Boolean)args[1]; - long dialog_id = (Long)args[0]; + boolean close = (Boolean) args[1]; + long dialog_id = (Long) args[0]; if (close) { if (dialog_id == openedDialogId) { openedDialogId = 0; @@ -740,22 +781,22 @@ public class MessagesActivity extends BaseFragment implements NotificationCenter } private void updateVisibleRows(int mask) { - if (messagesListView == null) { + if (listView == null) { return; } - int count = messagesListView.getChildCount(); + int count = listView.getChildCount(); for (int a = 0; a < count; a++) { - View child = messagesListView.getChildAt(a); + View child = listView.getChildAt(a); if (child instanceof DialogCell) { DialogCell cell = (DialogCell) child; if ((mask & MessagesController.UPDATE_MASK_NEW_MESSAGE) != 0) { cell.checkCurrentDialogIndex(); if (!serverOnly && AndroidUtilities.isTablet()) { - child.setBackgroundColor(cell.getDialogId() == openedDialogId ? 0x0f000000 : 0); + cell.setDialogSelected(cell.getDialogId() == openedDialogId); } } else if ((mask & MessagesController.UPDATE_MASK_SELECT_DIALOG) != 0) { if (!serverOnly && AndroidUtilities.isTablet()) { - child.setBackgroundColor(cell.getDialogId() == openedDialogId ? 0x0f000000 : 0); + cell.setDialogSelected(cell.getDialogId() == openedDialogId); } } else { cell.update(mask); @@ -785,8 +826,8 @@ public class MessagesActivity extends BaseFragment implements NotificationCenter } AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); - int lower_part = (int)dialog_id; - int high_id = (int)(dialog_id >> 32); + int lower_part = (int) dialog_id; + int high_id = (int) (dialog_id >> 32); if (lower_part != 0) { if (high_id == 1) { TLRPC.Chat chat = MessagesController.getInstance().getChat(lower_part); @@ -817,29 +858,15 @@ public class MessagesActivity extends BaseFragment implements NotificationCenter } builder.setMessage(LocaleController.formatStringSimple(selectAlertString, ContactsController.formatName(user.first_name, user.last_name))); } - CheckBox checkBox = null; - /*if (delegate instanceof ChatActivity) { - checkBox = new CheckBox(getParentActivity()); - checkBox.setText(LocaleController.getString("ForwardFromMyName", R.string.ForwardFromMyName)); - checkBox.setChecked(false); - builder.setView(checkBox); - }*/ - final CheckBox checkBoxFinal = checkBox; + builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { - didSelectResult(dialog_id, false, checkBoxFinal != null && checkBoxFinal.isChecked()); + didSelectResult(dialog_id, false, false); } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); - if (checkBox != null) { - ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams)checkBox.getLayoutParams(); - if (layoutParams != null) { - layoutParams.rightMargin = layoutParams.leftMargin = AndroidUtilities.dp(10); - checkBox.setLayoutParams(layoutParams); - } - } + showDialog(builder.create()); } else { if (delegate != null) { delegate.didSelectDialog(MessagesActivity.this, dialog_id, param); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSettingsActivity.java index a8f3bfd52..a6d162c9a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSettingsActivity.java @@ -19,11 +19,13 @@ import android.media.RingtoneManager; import android.net.Uri; import android.os.Build; import android.provider.Settings; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.FrameLayout; +import android.widget.LinearLayout; import android.widget.ListView; import android.widget.Toast; @@ -48,6 +50,7 @@ import org.telegram.ui.Cells.TextDetailSettingsCell; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.Components.ColorPickerView; +import org.telegram.ui.Components.LayoutHelper; public class NotificationsSettingsActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { private ListView listView; @@ -178,8 +181,8 @@ public class NotificationsSettingsActivity extends BaseFragment implements Notif listView.setVerticalScrollBarEnabled(false); frameLayout.addView(listView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; listView.setLayoutParams(layoutParams); listView.setAdapter(new ListAdapter(context)); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @@ -355,16 +358,17 @@ public class NotificationsSettingsActivity extends BaseFragment implements Notif } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } } else if (i == messageLedRow || i == groupLedRow) { if (getParentActivity() == null) { return; } - LayoutInflater li = (LayoutInflater) getParentActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); - view = li.inflate(R.layout.settings_color_dialog_layout, null, false); - final ColorPickerView colorPickerView = (ColorPickerView) view.findViewById(R.id.color_picker); + LinearLayout linearLayout = new LinearLayout(getParentActivity()); + linearLayout.setOrientation(LinearLayout.VERTICAL); + final ColorPickerView colorPickerView = new ColorPickerView(getParentActivity()); + linearLayout.addView(colorPickerView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); if (i == messageLedRow) { @@ -375,7 +379,7 @@ public class NotificationsSettingsActivity extends BaseFragment implements Notif AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); builder.setTitle(LocaleController.getString("LedColor", R.string.LedColor)); - builder.setView(view); + builder.setView(linearLayout); builder.setPositiveButton(LocaleController.getString("Set", R.string.Set), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int which) { @@ -404,7 +408,7 @@ public class NotificationsSettingsActivity extends BaseFragment implements Notif listView.invalidateViews(); } }); - showAlertDialog(builder); + showDialog(builder.create()); } else if (i == messagePopupNotificationRow || i == groupPopupNotificationRow) { AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); builder.setTitle(LocaleController.getString("PopupNotification", R.string.PopupNotification)); @@ -430,7 +434,7 @@ public class NotificationsSettingsActivity extends BaseFragment implements Notif } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } else if (i == messageVibrateRow || i == groupVibrateRow) { AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); builder.setTitle(LocaleController.getString("Vibrate", R.string.Vibrate)); @@ -467,7 +471,7 @@ public class NotificationsSettingsActivity extends BaseFragment implements Notif } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } else if (i == messagePriorityRow || i == groupPriorityRow) { AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); builder.setTitle(LocaleController.getString("NotificationsPriority", R.string.NotificationsPriority)); @@ -490,7 +494,7 @@ public class NotificationsSettingsActivity extends BaseFragment implements Notif } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } else if (i == repeatRow) { AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); builder.setTitle(LocaleController.getString("RepeatNotifications", R.string.RepeatNotifications)); @@ -527,7 +531,7 @@ public class NotificationsSettingsActivity extends BaseFragment implements Notif } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } if (view instanceof TextCheckCell) { ((TextCheckCell) view).setChecked(!enabled); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PasscodeActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PasscodeActivity.java index f5cad11a8..49333c45b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PasscodeActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PasscodeActivity.java @@ -37,7 +37,6 @@ import android.view.inputmethod.EditorInfo; import android.widget.AdapterView; import android.widget.EditText; import android.widget.FrameLayout; -import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; @@ -58,6 +57,7 @@ import org.telegram.ui.Adapters.BaseFragmentAdapter; import org.telegram.ui.Cells.TextCheckCell; import org.telegram.ui.Cells.TextInfoPrivacyCell; import org.telegram.ui.Cells.TextSettingsCell; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.NumberPicker; public class PasscodeActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { @@ -156,8 +156,8 @@ public class PasscodeActivity extends BaseFragment implements NotificationCenter titleTextView.setGravity(Gravity.CENTER_HORIZONTAL); frameLayout.addView(titleTextView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) titleTextView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER_HORIZONTAL; layoutParams.topMargin = AndroidUtilities.dp(38); titleTextView.setLayoutParams(layoutParams); @@ -184,7 +184,7 @@ public class PasscodeActivity extends BaseFragment implements NotificationCenter layoutParams.leftMargin = AndroidUtilities.dp(40); layoutParams.gravity = Gravity.TOP | Gravity.LEFT; layoutParams.rightMargin = AndroidUtilities.dp(40); - layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; passwordEditText.setLayoutParams(layoutParams); passwordEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override @@ -257,8 +257,8 @@ public class PasscodeActivity extends BaseFragment implements NotificationCenter dropDownContainer.addSubItem(password_item, LocaleController.getString("PasscodePassword", R.string.PasscodePassword), 0); actionBar.addView(dropDownContainer); layoutParams = (FrameLayout.LayoutParams) dropDownContainer.getLayoutParams(); - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; layoutParams.rightMargin = AndroidUtilities.dp(40); layoutParams.leftMargin = AndroidUtilities.isTablet() ? AndroidUtilities.dp(64) : AndroidUtilities.dp(56); layoutParams.gravity = Gravity.TOP | Gravity.LEFT; @@ -283,8 +283,8 @@ public class PasscodeActivity extends BaseFragment implements NotificationCenter dropDown.setPadding(0, 0, AndroidUtilities.dp(10), 0); dropDownContainer.addView(dropDown); layoutParams = (FrameLayout.LayoutParams) dropDown.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(16); layoutParams.gravity = Gravity.CENTER_VERTICAL; layoutParams.bottomMargin = AndroidUtilities.dp(1); @@ -304,8 +304,8 @@ public class PasscodeActivity extends BaseFragment implements NotificationCenter listView.setDrawSelectorOnTop(true); frameLayout.addView(listView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP; listView.setLayoutParams(layoutParams); listView.setAdapter(listAdapter = new ListAdapter(context)); @@ -391,7 +391,7 @@ public class PasscodeActivity extends BaseFragment implements NotificationCenter UserConfig.saveConfig(false); } }); - showAlertDialog(builder); + showDialog(builder.create()); } } }); @@ -525,16 +525,28 @@ public class PasscodeActivity extends BaseFragment implements NotificationCenter passwordEditText.setText(""); return; } - UserConfig.passcodeHash = Utilities.MD5(firstPassword); + + try { + UserConfig.passcodeSalt = new byte[16]; + Utilities.random.nextBytes(UserConfig.passcodeSalt); + byte[] passcodeBytes = firstPassword.getBytes("UTF-8"); + byte[] bytes = new byte[32 + passcodeBytes.length]; + System.arraycopy(UserConfig.passcodeSalt, 0, bytes, 0, 16); + System.arraycopy(passcodeBytes, 0, bytes, 16, passcodeBytes.length); + System.arraycopy(UserConfig.passcodeSalt, 0, bytes, passcodeBytes.length + 16, 16); + UserConfig.passcodeHash = Utilities.bytesToHex(Utilities.computeSHA256(bytes, 0, bytes.length)); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + UserConfig.passcodeType = currentPasswordType; UserConfig.saveConfig(false); - //TODO show alert finishFragment(); NotificationCenter.getInstance().postNotificationName(NotificationCenter.didSetPasscode); passwordEditText.clearFocus(); AndroidUtilities.hideKeyboard(passwordEditText); } else if (type == 2) { - if (!Utilities.MD5(passwordEditText.getText().toString()).equals(UserConfig.passcodeHash)) { + if (!UserConfig.checkPasscode(passwordEditText.getText().toString())) { passwordEditText.setText(""); onPasscodeError(); return; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoAlbumPickerActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoAlbumPickerActivity.java index fa330b1db..7b4922103 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PhotoAlbumPickerActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoAlbumPickerActivity.java @@ -10,8 +10,10 @@ package org.telegram.ui; import android.app.Activity; import android.content.Context; +import android.content.res.Configuration; import android.graphics.drawable.ColorDrawable; import android.os.Build; +import android.text.TextUtils; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -34,10 +36,12 @@ import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.R; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; +import org.telegram.ui.ActionBar.ActionBarMenuItem; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.Adapters.BaseFragmentAdapter; import org.telegram.ui.Cells.PhotoPickerAlbumsCell; import org.telegram.ui.Cells.PhotoPickerSearchCell; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.PhotoPickerBottomLayout; import java.util.ArrayList; @@ -46,11 +50,13 @@ import java.util.HashMap; public class PhotoAlbumPickerActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { public interface PhotoAlbumPickerActivityDelegate { - void didSelectPhotos(ArrayList photos, ArrayList webPhotos); + void didSelectPhotos(ArrayList photos, ArrayList captions, ArrayList webPhotos); + boolean didSelectVideo(String path); void startPhotoSelectActivity(); } private ArrayList albumsSorted = null; + private ArrayList videoAlbumsSorted = null; private HashMap selectedPhotos = new HashMap<>(); private HashMap selectedWebPhotos = new HashMap<>(); private HashMap recentImagesWebKeys = new HashMap<>(); @@ -64,15 +70,23 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati private ListAdapter listAdapter; private FrameLayout progressView; private TextView emptyView; + private TextView dropDown; + private ActionBarMenuItem dropDownContainer; private PhotoPickerBottomLayout photoPickerBottomLayout; private boolean sendPressed = false; private boolean singlePhoto = false; + private int selectedMode; + private ChatActivity chatActivity; private PhotoAlbumPickerActivityDelegate delegate; - public PhotoAlbumPickerActivity(boolean onlyOnePhoto) { + private final static int item_photos = 2; + private final static int item_video = 3; + + public PhotoAlbumPickerActivity(boolean singlePhoto, ChatActivity chatActivity) { super(); - singlePhoto = onlyOnePhoto; + this.chatActivity = chatActivity; + this.singlePhoto = singlePhoto; } @Override @@ -99,7 +113,6 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati actionBar.setBackgroundColor(0xff333333); actionBar.setItemsBackground(R.drawable.bar_selector_picker); actionBar.setBackButtonImage(R.drawable.ic_ab_back); - actionBar.setTitle(LocaleController.getString("Gallery", R.string.Gallery)); actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @Override public void onItemClick(int id) { @@ -115,6 +128,22 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati finishFragment(false); delegate.startPhotoSelectActivity(); } + } else if (id == item_photos) { + if (selectedMode == 0) { + return; + } + selectedMode = 0; + dropDown.setText(LocaleController.getString("PickerPhotos", R.string.PickerPhotos)); + emptyView.setText(LocaleController.getString("NoPhotos", R.string.NoPhotos)); + listAdapter.notifyDataSetChanged(); + } else if (id == item_video) { + if (selectedMode == 1) { + return; + } + selectedMode = 1; + dropDown.setText(LocaleController.getString("PickerVideo", R.string.PickerVideo)); + emptyView.setText(LocaleController.getString("NoVideo", R.string.NoVideo)); + listAdapter.notifyDataSetChanged(); } } }); @@ -127,6 +156,51 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati FrameLayout frameLayout = (FrameLayout) fragmentView; frameLayout.setBackgroundColor(0xff000000); + if (!singlePhoto) { + selectedMode = 0; + + dropDownContainer = new ActionBarMenuItem(context, menu, R.drawable.bar_selector_picker); + dropDownContainer.setSubMenuOpenSide(1); + dropDownContainer.addSubItem(item_photos, LocaleController.getString("PickerPhotos", R.string.PickerPhotos), 0); + dropDownContainer.addSubItem(item_video, LocaleController.getString("PickerVideo", R.string.PickerVideo), 0); + actionBar.addView(dropDownContainer); + FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) dropDownContainer.getLayoutParams(); + layoutParams.height = LayoutHelper.MATCH_PARENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.rightMargin = AndroidUtilities.dp(40); + layoutParams.leftMargin = AndroidUtilities.isTablet() ? AndroidUtilities.dp(64) : AndroidUtilities.dp(56); + layoutParams.gravity = Gravity.TOP | Gravity.LEFT; + dropDownContainer.setLayoutParams(layoutParams); + dropDownContainer.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + dropDownContainer.toggleSubMenu(); + } + }); + + dropDown = new TextView(context); + dropDown.setGravity(Gravity.LEFT); + dropDown.setSingleLine(true); + dropDown.setLines(1); + dropDown.setMaxLines(1); + dropDown.setEllipsize(TextUtils.TruncateAt.END); + dropDown.setTextColor(0xffffffff); + dropDown.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + dropDown.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_arrow_drop_down, 0); + dropDown.setCompoundDrawablePadding(AndroidUtilities.dp(4)); + dropDown.setPadding(0, 0, AndroidUtilities.dp(10), 0); + dropDown.setText(LocaleController.getString("PickerPhotos", R.string.PickerPhotos)); + dropDownContainer.addView(dropDown); + layoutParams = (FrameLayout.LayoutParams) dropDown.getLayoutParams(); + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; + layoutParams.leftMargin = AndroidUtilities.dp(16); + layoutParams.gravity = Gravity.CENTER_VERTICAL; + dropDown.setLayoutParams(layoutParams); + } else { + actionBar.setTitle(LocaleController.getString("Gallery", R.string.Gallery)); + } + listView = new ListView(context); listView.setPadding(AndroidUtilities.dp(4), 0, AndroidUtilities.dp(4), AndroidUtilities.dp(4)); listView.setClipToPadding(false); @@ -139,8 +213,8 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati listView.setScrollingCacheEnabled(false); frameLayout.addView(listView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.bottomMargin = AndroidUtilities.dp(48); listView.setLayoutParams(layoutParams); listView.setAdapter(listAdapter = new ListAdapter(context)); @@ -154,8 +228,8 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati emptyView.setText(LocaleController.getString("NoPhotos", R.string.NoPhotos)); frameLayout.addView(emptyView); layoutParams = (FrameLayout.LayoutParams) emptyView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.bottomMargin = AndroidUtilities.dp(48); emptyView.setLayoutParams(layoutParams); emptyView.setOnTouchListener(new View.OnTouchListener() { @@ -169,23 +243,23 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati progressView.setVisibility(View.GONE); frameLayout.addView(progressView); layoutParams = (FrameLayout.LayoutParams) progressView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.bottomMargin = AndroidUtilities.dp(48); progressView.setLayoutParams(layoutParams); ProgressBar progressBar = new ProgressBar(context); progressView.addView(progressBar); layoutParams = (FrameLayout.LayoutParams) progressView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER; progressView.setLayoutParams(layoutParams); photoPickerBottomLayout = new PhotoPickerBottomLayout(context); frameLayout.addView(photoPickerBottomLayout); layoutParams = (FrameLayout.LayoutParams) photoPickerBottomLayout.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(48); layoutParams.gravity = Gravity.BOTTOM; photoPickerBottomLayout.setLayoutParams(layoutParams); @@ -237,6 +311,7 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati int guid = (Integer) args[0]; if (classGuid == guid) { albumsSorted = (ArrayList) args[1]; + videoAlbumsSorted = (ArrayList) args[3]; if (progressView != null) { progressView.setVisibility(View.GONE); } @@ -278,12 +353,15 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati } sendPressed = true; ArrayList photos = new ArrayList<>(); + ArrayList captions = new ArrayList<>(); for (HashMap.Entry entry : selectedPhotos.entrySet()) { MediaController.PhotoEntry photoEntry = entry.getValue(); if (photoEntry.imagePath != null) { photos.add(photoEntry.imagePath); + captions.add(photoEntry.caption != null ? photoEntry.caption.toString() : null); } else if (photoEntry.path != null) { photos.add(photoEntry.path); + captions.add(photoEntry.caption != null ? photoEntry.caption.toString() : null); } } ArrayList webPhotos = new ArrayList<>(); @@ -293,6 +371,7 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati MediaController.SearchImage searchImage = entry.getValue(); if (searchImage.imagePath != null) { photos.add(searchImage.imagePath); + captions.add(searchImage.caption != null ? searchImage.caption.toString() : null); } else { webPhotos.add(searchImage); } @@ -325,7 +404,7 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati MessagesStorage.getInstance().putWebRecent(recentGifImages); } - delegate.didSelectPhotos(photos, webPhotos); + delegate.didSelectPhotos(photos, captions, webPhotos); } private void fixLayout() { @@ -348,6 +427,7 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati if (getParentActivity() == null) { return; } + WindowManager manager = (WindowManager) ApplicationLoader.applicationContext.getSystemService(Activity.WINDOW_SERVICE); int rotation = manager.getDefaultDisplay().getRotation(); columnsCount = 2; @@ -355,6 +435,20 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati columnsCount = 4; } listAdapter.notifyDataSetChanged(); + + if (dropDownContainer != null) { + if (!AndroidUtilities.isTablet()) { + FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) dropDownContainer.getLayoutParams(); + layoutParams.topMargin = (Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0); + dropDownContainer.setLayoutParams(layoutParams); + } + + if (!AndroidUtilities.isTablet() && ApplicationLoader.applicationContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { + dropDown.setTextSize(18); + } else { + dropDown.setTextSize(20); + } + } } private void openPhotoPicker(MediaController.AlbumEntry albumEntry, int type) { @@ -366,7 +460,7 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati recentImages = recentGifImages; } } - PhotoPickerActivity fragment = new PhotoPickerActivity(type, albumEntry, selectedPhotos, selectedWebPhotos, recentImages, singlePhoto); + PhotoPickerActivity fragment = new PhotoPickerActivity(type, albumEntry, selectedPhotos, selectedWebPhotos, recentImages, singlePhoto, chatActivity); fragment.setDelegate(new PhotoPickerActivity.PhotoPickerActivityDelegate() { @Override public void selectedPhotosChanged() { @@ -382,6 +476,12 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati sendSelectedPhotos(); } } + + @Override + public boolean didSelectVideo(String path) { + removeSelfFromStack(); + return delegate.didSelectVideo(path); + } }); presentFragment(fragment); } @@ -405,10 +505,14 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati @Override public int getCount() { - if (singlePhoto) { - return albumsSorted != null ? (int) Math.ceil(albumsSorted.size() / (float) columnsCount) : 0; + if (singlePhoto || selectedMode == 0) { + if (singlePhoto) { + return albumsSorted != null ? (int) Math.ceil(albumsSorted.size() / (float) columnsCount) : 0; + } + return 1 + (albumsSorted != null ? (int) Math.ceil(albumsSorted.size() / (float) columnsCount) : 0); + } else { + return (videoAlbumsSorted != null ? (int) Math.ceil(videoAlbumsSorted.size() / (float) columnsCount) : 0); } - return 1 + (albumsSorted != null ? (int) Math.ceil(albumsSorted.size() / (float) columnsCount) : 0); } @Override @@ -430,7 +534,7 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati public View getView(int i, View view, ViewGroup viewGroup) { int type = getItemViewType(i); if (type == 0) { - PhotoPickerAlbumsCell photoPickerAlbumsCell = null; + PhotoPickerAlbumsCell photoPickerAlbumsCell; if (view == null) { view = new PhotoPickerAlbumsCell(mContext); photoPickerAlbumsCell = (PhotoPickerAlbumsCell) view; @@ -446,16 +550,25 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati photoPickerAlbumsCell.setAlbumsCount(columnsCount); for (int a = 0; a < columnsCount; a++) { int index; - if (singlePhoto) { + if (singlePhoto || selectedMode == 1) { index = i * columnsCount + a; } else { index = (i - 1) * columnsCount + a; } - if (index < albumsSorted.size()) { - MediaController.AlbumEntry albumEntry = albumsSorted.get(index); - photoPickerAlbumsCell.setAlbum(a, albumEntry); + if (singlePhoto || selectedMode == 0) { + if (index < albumsSorted.size()) { + MediaController.AlbumEntry albumEntry = albumsSorted.get(index); + photoPickerAlbumsCell.setAlbum(a, albumEntry); + } else { + photoPickerAlbumsCell.setAlbum(a, null); + } } else { - photoPickerAlbumsCell.setAlbum(a, null); + if (index < videoAlbumsSorted.size()) { + MediaController.AlbumEntry albumEntry = videoAlbumsSorted.get(index); + photoPickerAlbumsCell.setAlbum(a, albumEntry); + } else { + photoPickerAlbumsCell.setAlbum(a, null); + } } } photoPickerAlbumsCell.requestLayout(); @@ -475,7 +588,7 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati @Override public int getItemViewType(int i) { - if (singlePhoto) { + if (singlePhoto || selectedMode == 1) { return 0; } if (i == 0) { @@ -486,7 +599,7 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati @Override public int getViewTypeCount() { - if (singlePhoto) { + if (singlePhoto || selectedMode == 1) { return 1; } return 2; @@ -494,7 +607,7 @@ public class PhotoAlbumPickerActivity extends BaseFragment implements Notificati @Override public boolean isEmpty() { - return false; + return getCount() == 0; } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoCropActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoCropActivity.java index af94958c1..fd947b253 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PhotoCropActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoCropActivity.java @@ -29,6 +29,7 @@ import org.telegram.messenger.R; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; import java.io.File; @@ -461,7 +462,7 @@ public class PhotoCropActivity extends BaseFragment { fragmentView = view = new PhotoCropView(context); ((PhotoCropView) fragmentView).freeform = getArguments().getBoolean("freeform", false); - fragmentView.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); + fragmentView.setLayoutParams(new FrameLayout.LayoutParams(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); return fragmentView; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoPickerActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoPickerActivity.java index d58873cad..c737724f6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PhotoPickerActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoPickerActivity.java @@ -51,6 +51,7 @@ import org.telegram.messenger.FileLog; import org.telegram.messenger.R; import org.telegram.messenger.TLRPC; import org.telegram.android.MessageObject; +import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; @@ -59,6 +60,7 @@ import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.Cells.PhotoPickerPhotoCell; import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.PhotoPickerBottomLayout; import java.net.URLEncoder; @@ -72,6 +74,7 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen public interface PhotoPickerActivityDelegate { void selectedPhotosChanged(); void actionButtonPressed(boolean canceled); + boolean didSelectVideo(String path); } private RequestQueue requestQueue; @@ -102,10 +105,11 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen private int itemWidth = 100; private boolean sendPressed; private boolean singlePhoto; + private ChatActivity chatActivity; private PhotoPickerActivityDelegate delegate; - public PhotoPickerActivity(int type, MediaController.AlbumEntry selectedAlbum, HashMap selectedPhotos, HashMap selectedWebPhotos, ArrayList recentImages, boolean onlyOnePhoto) { + public PhotoPickerActivity(int type, MediaController.AlbumEntry selectedAlbum, HashMap selectedPhotos, HashMap selectedWebPhotos, ArrayList recentImages, boolean onlyOnePhoto, ChatActivity chatActivity) { super(); this.selectedAlbum = selectedAlbum; this.selectedPhotos = selectedPhotos; @@ -113,6 +117,10 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen this.type = type; this.recentImages = recentImages; this.singlePhoto = onlyOnePhoto; + this.chatActivity = chatActivity; + if (selectedAlbum != null && selectedAlbum.isVideo) { + singlePhoto = true; + } } @Override @@ -256,8 +264,8 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen listView.setSelector(R.drawable.list_selector); frameLayout.addView(listView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.bottomMargin = singlePhoto ? 0 : AndroidUtilities.dp(48); listView.setLayoutParams(layoutParams); listView.setAdapter(listAdapter = new ListAdapter(context)); @@ -265,21 +273,30 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView adapterView, View view, int i, long l) { - ArrayList arrayList = null; - if (selectedAlbum != null) { - arrayList = (ArrayList) selectedAlbum.photos; - } else { - if (searchResult.isEmpty() && lastSearchString == null) { - arrayList = (ArrayList) recentImages; - } else { - arrayList = (ArrayList) searchResult; + if (selectedAlbum != null && selectedAlbum.isVideo) { + if (i < 0 || i >= selectedAlbum.photos.size()) { + return; } + if (delegate.didSelectVideo(selectedAlbum.photos.get(i).path)) { + finishFragment(); + } + } else { + ArrayList arrayList; + if (selectedAlbum != null) { + arrayList = (ArrayList) selectedAlbum.photos; + } else { + if (searchResult.isEmpty() && lastSearchString == null) { + arrayList = (ArrayList) recentImages; + } else { + arrayList = (ArrayList) searchResult; + } + } + if (i < 0 || i >= arrayList.size()) { + return; + } + PhotoViewer.getInstance().setParentActivity(getParentActivity()); + PhotoViewer.getInstance().openPhotoForSelect(arrayList, i, singlePhoto ? 1 : 0, PhotoPickerActivity.this, chatActivity); } - if (i < 0 || i >= arrayList.size()) { - return; - } - PhotoViewer.getInstance().setParentActivity(getParentActivity()); - PhotoViewer.getInstance().openPhotoForSelect(arrayList, i, singlePhoto ? 1 : 0, PhotoPickerActivity.this); } }); @@ -302,7 +319,7 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); return true; } return false; @@ -326,8 +343,8 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen } frameLayout.addView(emptyView); layoutParams = (FrameLayout.LayoutParams) emptyView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.bottomMargin = singlePhoto ? 0 : AndroidUtilities.dp(48); emptyView.setLayoutParams(layoutParams); emptyView.setOnTouchListener(new View.OnTouchListener() { @@ -362,16 +379,16 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen progressView.setVisibility(View.GONE); frameLayout.addView(progressView); layoutParams = (FrameLayout.LayoutParams) progressView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.bottomMargin = singlePhoto ? 0 : AndroidUtilities.dp(48); progressView.setLayoutParams(layoutParams); ProgressBar progressBar = new ProgressBar(context); progressView.addView(progressBar); layoutParams = (FrameLayout.LayoutParams) progressBar.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER; progressBar.setLayoutParams(layoutParams); @@ -381,7 +398,7 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen photoPickerBottomLayout = new PhotoPickerBottomLayout(context); frameLayout.addView(photoPickerBottomLayout); layoutParams = (FrameLayout.LayoutParams) photoPickerBottomLayout.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = AndroidUtilities.dp(48); layoutParams.gravity = Gravity.BOTTOM; photoPickerBottomLayout.setLayoutParams(layoutParams); @@ -454,7 +471,7 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen continue; } } else { - ArrayList array = null; + ArrayList array; if (searchResult.isEmpty() && lastSearchString == null) { array = recentImages; } else { @@ -501,12 +518,16 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen cell.photoImage.setImage(photoEntry.thumbPath, null, cell.getContext().getResources().getDrawable(R.drawable.nophotos)); } else if (photoEntry.path != null) { cell.photoImage.setOrientation(photoEntry.orientation, true); - cell.photoImage.setImage("thumb://" + photoEntry.imageId + ":" + photoEntry.path, null, cell.getContext().getResources().getDrawable(R.drawable.nophotos)); + if (photoEntry.isVideo) { + cell.photoImage.setImage("vthumb://" + photoEntry.imageId + ":" + photoEntry.path, null, cell.getContext().getResources().getDrawable(R.drawable.nophotos)); + } else { + cell.photoImage.setImage("thumb://" + photoEntry.imageId + ":" + photoEntry.path, null, cell.getContext().getResources().getDrawable(R.drawable.nophotos)); + } } else { cell.photoImage.setImageResource(R.drawable.nophotos); } } else { - ArrayList array = null; + ArrayList array; if (searchResult.isEmpty() && lastSearchString == null) { array = recentImages; } else { @@ -548,7 +569,7 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen continue; } } else { - ArrayList array = null; + ArrayList array; if (searchResult.isEmpty() && lastSearchString == null) { array = recentImages; } else { @@ -577,7 +598,7 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen if (selectedAlbum != null) { return !(index < 0 || index >= selectedAlbum.photos.size()) && selectedPhotos.containsKey(selectedAlbum.photos.get(index).imageId); } else { - ArrayList array = null; + ArrayList array; if (searchResult.isEmpty() && lastSearchString == null) { array = recentImages; } else { @@ -602,8 +623,8 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen selectedPhotos.put(photoEntry.imageId, photoEntry); } } else { - MediaController.SearchImage photoEntry = null; - ArrayList array = null; + MediaController.SearchImage photoEntry; + ArrayList array; if (searchResult.isEmpty() && lastSearchString == null) { array = recentImages; } else { @@ -650,7 +671,7 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen selectedPhotos.put(photoEntry.imageId, photoEntry); } } else if (selectedPhotos.isEmpty()) { - ArrayList array = null; + ArrayList array; if (searchResult.isEmpty() && lastSearchString == null) { array = recentImages; } else { @@ -777,7 +798,10 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen if (nextSearchBingString != null) { url = nextSearchBingString; } else { - url = String.format(Locale.US, "https://api.datamarket.azure.com/Bing/Search/v1/Image?Query='%s'&$skip=%d&$top=%d&$format=json&Adult='Off'", URLEncoder.encode(query, "UTF-8"), offset, count); + boolean adult; + String phone = UserConfig.getCurrentUser().phone; + adult = phone.startsWith("44") || phone.startsWith("49") || phone.startsWith("43") || phone.startsWith("31") || phone.startsWith("1"); + url = String.format(Locale.US, "https://api.datamarket.azure.com/Bing/Search/v1/Image?Query='%s'&$skip=%d&$top=%d&$format=json%s", URLEncoder.encode(query, "UTF-8"), offset, count, adult ? "" : "&Adult='Off'"); } JsonObjectRequest jsonObjReq = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener() { @@ -889,7 +913,7 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen WindowManager manager = (WindowManager) ApplicationLoader.applicationContext.getSystemService(Activity.WINDOW_SERVICE); int rotation = manager.getDefaultDisplay().getRotation(); - int columnsCount = 2; + int columnsCount; if (AndroidUtilities.isTablet()) { columnsCount = 3; } else { @@ -993,7 +1017,7 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen ((PhotoPickerPhotoCell) v.getParent()).checkBox.setChecked(selectedPhotos.containsKey(photoEntry.imageId), true); } else { AndroidUtilities.hideKeyboard(getParentActivity().getCurrentFocus()); - MediaController.SearchImage photoEntry = null; + MediaController.SearchImage photoEntry; if (searchResult.isEmpty() && lastSearchString == null) { photoEntry = recentImages.get((Integer)((View)v.getParent()).getTag()); } else { @@ -1019,7 +1043,7 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen BackupImageView imageView = ((PhotoPickerPhotoCell) view).photoImage; imageView.setTag(i); view.setTag(i); - boolean showing = false; + boolean showing; imageView.setOrientation(0, true); if (selectedAlbum != null) { @@ -1028,14 +1052,18 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen imageView.setImage(photoEntry.thumbPath, null, mContext.getResources().getDrawable(R.drawable.nophotos)); } else if (photoEntry.path != null) { imageView.setOrientation(photoEntry.orientation, true); - imageView.setImage("thumb://" + photoEntry.imageId + ":" + photoEntry.path, null, mContext.getResources().getDrawable(R.drawable.nophotos)); + if (photoEntry.isVideo) { + imageView.setImage("vthumb://" + photoEntry.imageId + ":" + photoEntry.path, null, mContext.getResources().getDrawable(R.drawable.nophotos)); + } else { + imageView.setImage("thumb://" + photoEntry.imageId + ":" + photoEntry.path, null, mContext.getResources().getDrawable(R.drawable.nophotos)); + } } else { imageView.setImageResource(R.drawable.nophotos); } cell.checkBox.setChecked(selectedPhotos.containsKey(photoEntry.imageId), false); showing = PhotoViewer.getInstance().isShowingImage(photoEntry.path); } else { - MediaController.SearchImage photoEntry = null; + MediaController.SearchImage photoEntry; if (searchResult.isEmpty() && lastSearchString == null) { photoEntry = recentImages.get(i); } else { @@ -1051,7 +1079,7 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen cell.checkBox.setChecked(selectedWebPhotos.containsKey(photoEntry.id), false); showing = PhotoViewer.getInstance().isShowingImage(photoEntry.thumbUrl); } - imageView.getImageReceiver().setVisible(!showing, false); + imageView.getImageReceiver().setVisible(!showing, true); cell.checkBox.setVisibility(singlePhoto || showing ? View.GONE : View.VISIBLE); } else if (viewType == 1) { if (view == null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java index 79bd4d4cb..270962eda 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java @@ -30,16 +30,19 @@ import android.text.TextUtils; import android.util.TypedValue; import android.view.GestureDetector; import android.view.Gravity; +import android.view.KeyEvent; import android.view.MotionEvent; import android.view.Surface; import android.view.VelocityTracker; import android.view.View; import android.view.ViewGroup; -import android.view.ViewTreeObserver; import android.view.WindowManager; import android.view.animation.DecelerateInterpolator; +import android.widget.AdapterView; import android.widget.FrameLayout; import android.widget.ImageView; +import android.widget.ListView; +import android.widget.RelativeLayout; import android.widget.Scroller; import android.widget.TextView; @@ -61,10 +64,11 @@ import org.telegram.messenger.TLRPC; import org.telegram.messenger.UserConfig; import org.telegram.android.MessageObject; import org.telegram.messenger.Utilities; -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 org.telegram.ui.Adapters.MentionsAdapter; +import org.telegram.android.AnimationCompat.AnimatorListenerAdapterProxy; +import org.telegram.android.AnimationCompat.AnimatorSetProxy; +import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; +import org.telegram.android.AnimationCompat.ViewProxy; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; @@ -72,9 +76,12 @@ import org.telegram.ui.Components.CheckBox; import org.telegram.ui.Components.ClippingImageView; import org.telegram.android.ImageReceiver; import org.telegram.ui.Components.GifDrawable; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.PhotoCropView; import org.telegram.ui.Components.PhotoFilterView; import org.telegram.ui.Components.PhotoPickerBottomLayout; +import org.telegram.ui.Components.PhotoViewerCaptionEnterView; +import org.telegram.ui.Components.SizeNotifierRelativeLayoutPhoto; import java.io.File; import java.lang.ref.WeakReference; @@ -88,14 +95,14 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private int classGuid; private PhotoViewerProvider placeProvider; - private boolean isVisible = false; + private boolean isVisible; private Activity parentActivity; private ActionBar actionBar; private boolean isActionBarVisible = true; - private static Drawable[] progressDrawables = null; + private static Drawable[] progressDrawables; private WindowManager.LayoutParams windowLayoutParams; private FrameLayoutDrawer containerView; @@ -106,7 +113,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private TextView dateTextView; private ActionBarMenuItem menuItem; private ImageView shareButton; - private ColorDrawable backgroundDrawable = new ColorDrawable(0xff000000); + private BackgroundDrawable backgroundDrawable = new BackgroundDrawable(0xff000000); private CheckBox checkImageView; private PhotoPickerBottomLayout pickerView; private PhotoPickerBottomLayout editorDoneLayout; @@ -114,12 +121,27 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private GifDrawable gifDrawable; private ActionBarMenuItem cropItem; private ActionBarMenuItem tuneItem; + private ActionBarMenuItem captionItem; + private ActionBarMenuItem captionDoneItem; private AnimatorSetProxy currentActionBarAnimation; private PhotoCropView photoCropView; private PhotoFilterView photoFilterView; - private AlertDialog visibleDialog = null; + private AlertDialog visibleDialog; + private TextView captionTextView; + private TextView captionTextViewOld; + private TextView captionTextViewNew; + private PhotoViewerCaptionEnterView captionEditText; private boolean canShowBottom = true; private int sendPhotoType = 0; + private boolean needCaptionLayout; + + private float animationValues[][] = new float[2][8]; + + private ChatActivity parentChatActivity; + private MentionsAdapter mentionsAdapter; + private ListView mentionListView; + private AnimatorSetProxy mentionListAnimation; + private boolean allowMentions; private int animationInProgress = 0; private long transitionAnimationStartTime = 0; @@ -128,6 +150,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private PlaceProviderObject hideAfterAnimation; private boolean disableShowCheck = false; + private String lastTitle; + private int currentEditMode; private ImageReceiver leftImage = new ImageReceiver(); @@ -161,6 +185,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private float animationValue; private long animationStartTime; private AnimatorSetProxy imageMoveAnimation; + private AnimatorSetProxy changeModeAnimation; private GestureDetector gestureDetector; private DecelerateInterpolator interpolator = new DecelerateInterpolator(1.5f); private float pinchStartDistance = 0; @@ -204,10 +229,43 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private final static int gallery_menu_crop = 4; private final static int gallery_menu_delete = 6; private final static int gallery_menu_tune = 7; + private final static int gallery_menu_caption = 8; + private final static int gallery_menu_caption_done = 9; private final static int PAGE_SPACING = AndroidUtilities.dp(30); - private static class RadialProgressView { + private static DecelerateInterpolator decelerateInterpolator = null; + private static Paint progressPaint = null; + + private class BackgroundDrawable extends ColorDrawable { + + private Runnable drawRunnable; + + public BackgroundDrawable(int color) { + super(color); + } + + @Override + public void setAlpha(int alpha) { + if (parentActivity instanceof LaunchActivity) { + ((LaunchActivity) parentActivity).drawerLayoutContainer.setAllowDrawContent(!isVisible || alpha != 255); + } + super.setAlpha(alpha); + } + + @Override + public void draw(Canvas canvas) { + super.draw(canvas); + if (getAlpha() != 0) { + if (drawRunnable != null) { + drawRunnable.run(); + drawRunnable = null; + } + } + } + } + + private class RadialProgressView { private long lastUpdateTime = 0; private float radOffset = 0; @@ -224,9 +282,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private float alpha = 1.0f; private float scale = 1.0f; - private static DecelerateInterpolator decelerateInterpolator = null; - private static Paint progressPaint = null; - public RadialProgressView(Context context, View parentView) { if (decelerateInterpolator == null) { decelerateInterpolator = new DecelerateInterpolator(1.5f); @@ -302,8 +357,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat public void onDraw(Canvas canvas) { int sizeScaled = (int) (size * scale); - int x = (canvas.getWidth() - sizeScaled) / 2; - int y = (canvas.getHeight() - sizeScaled) / 2; + int x = (getContainerViewWidth() - sizeScaled) / 2; + int y = (getContainerViewHeight() - sizeScaled) / 2; if (previousBackgroundState >= 0 && previousBackgroundState < 4) { Drawable drawable = progressDrawables[previousBackgroundState]; @@ -428,6 +483,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } private class FrameLayoutTouchListener extends FrameLayout { + + private boolean attachedToWindow; + private Runnable attachRunnable; + public FrameLayoutTouchListener(Context context) { super(context); } @@ -442,9 +501,21 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat super.onLayout(changed, left, top, right, bottom); getInstance().onLayout(changed, left, top, right, bottom); } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + attachedToWindow = true; + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + attachedToWindow = false; + } } - private class FrameLayoutDrawer extends FrameLayout { + private class FrameLayoutDrawer extends SizeNotifierRelativeLayoutPhoto { public FrameLayoutDrawer(Context context) { super(context); setWillNotDraw(false); @@ -454,6 +525,27 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat protected void onDraw(Canvas canvas) { getInstance().onDraw(canvas); } + + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + if ((child == captionEditText || child == pickerView || child == captionTextView || child == mentionListView)) { + int state = captionEditText.getKeyboardTransitionState(); + if (!(state == 0 || state == 1 || state == 2)) { + if (child == captionTextView) { + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + if (captionTextView != null) { + captionTextView.invalidate(); + } + } + }, 50); + } + return false; + } + } + return super.drawChild(canvas, child, drawingTime); + } } private static volatile PhotoViewer Instance = null; @@ -668,10 +760,14 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } } } + } else if (id == NotificationCenter.emojiDidLoaded) { + if (captionTextView != null) { + captionTextView.invalidate(); + } } } - public void setParentActivity(Activity activity) { + public void setParentActivity(final Activity activity) { if (parentActivity == activity) { return; } @@ -687,21 +783,30 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat scroller = new Scroller(activity); - windowView = new FrameLayoutTouchListener(activity); + windowView = new FrameLayoutTouchListener(activity) { + @Override + public boolean dispatchKeyEventPreIme(KeyEvent event) { + if (event != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) { + if (captionEditText.isEmojiPopupShowing() || captionEditText.isKeyboardVisible()) { + closeCaptionEnter(false); + return false; + } + PhotoViewer.getInstance().closePhoto(true, false); + return true; + } + return super.dispatchKeyEventPreIme(event); + } + }; windowView.setBackgroundDrawable(backgroundDrawable); windowView.setFocusable(false); - animatingImageView = new ClippingImageView(windowView.getContext()); - windowView.addView(animatingImageView); + animatingImageView = new ClippingImageView(activity); + animatingImageView.setAnimationValues(animationValues); + windowView.addView(animatingImageView, LayoutHelper.createFrame(40, 40)); containerView = new FrameLayoutDrawer(activity); containerView.setFocusable(false); - windowView.addView(containerView); - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) containerView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.gravity = Gravity.TOP | Gravity.LEFT; - containerView.setLayoutParams(layoutParams); + windowView.addView(containerView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT)); windowLayoutParams = new WindowManager.LayoutParams(); windowLayoutParams.height = WindowManager.LayoutParams.MATCH_PARENT; @@ -717,15 +822,16 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat actionBar.setItemsBackground(R.drawable.bar_selector_white); actionBar.setBackButtonImage(R.drawable.ic_ab_back); actionBar.setTitle(LocaleController.formatString("Of", R.string.Of, 1, 1)); - containerView.addView(actionBar); - layoutParams = (FrameLayout.LayoutParams) actionBar.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - actionBar.setLayoutParams(layoutParams); + containerView.addView(actionBar, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @Override public void onItemClick(int id) { if (id == -1) { + if (needCaptionLayout && (captionEditText.isEmojiPopupShowing() || captionEditText.isKeyboardVisible())) { + closeCaptionEnter(false); + return; + } closePhoto(true, false); } else if (id == gallery_menu_save) { File f = null; @@ -875,6 +981,23 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); showAlertDialog(builder); + } else if (id == gallery_menu_caption) { + if (imageMoveAnimation != null || changeModeAnimation != null) { + return; + } + cropItem.setVisibility(View.GONE); + tuneItem.setVisibility(View.GONE); + captionItem.setVisibility(View.GONE); + checkImageView.setVisibility(View.GONE); + captionDoneItem.setVisibility(View.VISIBLE); + pickerView.setVisibility(View.GONE); + captionTextView.clearAnimation(); + captionTextView.setVisibility(View.INVISIBLE); + captionEditText.openKeyboard(); + lastTitle = actionBar.getTitle(); + actionBar.setTitle(LocaleController.getString("PhotoCaption", R.string.PhotoCaption)); + } else if (id == gallery_menu_caption_done) { + closeCaptionEnter(true); } } @@ -898,22 +1021,40 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat ActionBarMenu menu = actionBar.createMenu(); menuItem = menu.addItem(0, R.drawable.ic_ab_other); - menuItem.setNeedOffset(false); menuItem.addSubItem(gallery_menu_showall, LocaleController.getString("ShowAllMedia", R.string.ShowAllMedia), 0); menuItem.addSubItem(gallery_menu_save, LocaleController.getString("SaveToGallery", R.string.SaveToGallery), 0); menuItem.addSubItem(gallery_menu_delete, LocaleController.getString("Delete", R.string.Delete), 0); + captionDoneItem = menu.addItemWithWidth(gallery_menu_caption_done, R.drawable.ic_done, AndroidUtilities.dp(56)); + captionItem = menu.addItemWithWidth(gallery_menu_caption, R.drawable.photo_text, AndroidUtilities.dp(56)); cropItem = menu.addItemWithWidth(gallery_menu_crop, R.drawable.photo_crop, AndroidUtilities.dp(56)); - tuneItem = menu.addItemWithWidth(gallery_menu_tune, R.drawable.tune, AndroidUtilities.dp(56)); + tuneItem = menu.addItemWithWidth(gallery_menu_tune, R.drawable.photo_tools, AndroidUtilities.dp(56)); - bottomLayout = new FrameLayout(containerView.getContext()); - containerView.addView(bottomLayout); - layoutParams = (FrameLayout.LayoutParams) bottomLayout.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = AndroidUtilities.dp(48); - layoutParams.gravity = Gravity.BOTTOM | Gravity.LEFT; - bottomLayout.setLayoutParams(layoutParams); + bottomLayout = new FrameLayout(parentActivity); bottomLayout.setBackgroundColor(0x7f000000); + containerView.addView(bottomLayout, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, 48, RelativeLayout.ALIGN_PARENT_BOTTOM)); + + captionTextViewOld = new TextView(parentActivity); + captionTextViewOld.setMaxLines(10); + captionTextViewOld.setBackgroundColor(0x7f000000); + captionTextViewOld.setPadding(AndroidUtilities.dp(16), AndroidUtilities.dp(8), AndroidUtilities.dp(16), AndroidUtilities.dp(8)); + captionTextViewOld.setLinkTextColor(0xffffffff); + captionTextViewOld.setTextColor(0xffffffff); + captionTextViewOld.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT); + captionTextViewOld.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + captionTextViewOld.setVisibility(View.INVISIBLE); + containerView.addView(captionTextViewOld, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 0, 48, RelativeLayout.ALIGN_PARENT_BOTTOM)); + + captionTextView = captionTextViewNew = new TextView(parentActivity); + captionTextViewNew.setMaxLines(10); + captionTextViewNew.setBackgroundColor(0x7f000000); + captionTextViewNew.setPadding(AndroidUtilities.dp(16), AndroidUtilities.dp(8), AndroidUtilities.dp(16), AndroidUtilities.dp(8)); + captionTextViewNew.setLinkTextColor(0xffffffff); + captionTextViewNew.setTextColor(0xffffffff); + captionTextViewNew.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT); + captionTextViewNew.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + captionTextViewNew.setVisibility(View.INVISIBLE); + containerView.addView(captionTextViewNew, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 0, 48, RelativeLayout.ALIGN_PARENT_BOTTOM)); radialProgressViews[0] = new RadialProgressView(containerView.getContext(), containerView); radialProgressViews[0].setBackgroundState(0, false); @@ -926,12 +1067,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat shareButton.setImageResource(R.drawable.share); shareButton.setScaleType(ImageView.ScaleType.CENTER); shareButton.setBackgroundResource(R.drawable.bar_selector_white); - bottomLayout.addView(shareButton); - layoutParams = (FrameLayout.LayoutParams) shareButton.getLayoutParams(); - layoutParams.width = AndroidUtilities.dp(50); - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.gravity = Gravity.TOP | Gravity.RIGHT; - shareButton.setLayoutParams(layoutParams); + bottomLayout.addView(shareButton, LayoutHelper.createFrame(50, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.RIGHT)); shareButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -978,15 +1114,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat nameTextView.setEllipsize(TextUtils.TruncateAt.END); nameTextView.setTextColor(0xffffffff); nameTextView.setGravity(Gravity.LEFT); - bottomLayout.addView(nameTextView); - layoutParams = (FrameLayout.LayoutParams) nameTextView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.TOP | Gravity.LEFT; - layoutParams.leftMargin = AndroidUtilities.dp(16); - layoutParams.rightMargin = AndroidUtilities.dp(50); - layoutParams.topMargin = AndroidUtilities.dp(5); - nameTextView.setLayoutParams(layoutParams); + bottomLayout.addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.LEFT, 16, 5, 60, 0)); dateTextView = new TextView(containerView.getContext()); dateTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); @@ -996,24 +1124,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat dateTextView.setTextColor(0xffffffff); dateTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); dateTextView.setGravity(Gravity.LEFT); - bottomLayout.addView(dateTextView); - layoutParams = (FrameLayout.LayoutParams) dateTextView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.gravity = Gravity.TOP | Gravity.LEFT; - layoutParams.leftMargin = AndroidUtilities.dp(16); - layoutParams.rightMargin = AndroidUtilities.dp(50); - layoutParams.topMargin = AndroidUtilities.dp(25); - dateTextView.setLayoutParams(layoutParams); + bottomLayout.addView(dateTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.LEFT, 16, 25, 50, 0)); pickerView = new PhotoPickerBottomLayout(parentActivity); pickerView.setBackgroundColor(0x7f000000); - containerView.addView(pickerView); - layoutParams = (FrameLayout.LayoutParams) pickerView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = AndroidUtilities.dp(48); - layoutParams.gravity = Gravity.BOTTOM; - pickerView.setLayoutParams(layoutParams); + containerView.addView(pickerView, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, 48, RelativeLayout.ALIGN_PARENT_BOTTOM)); pickerView.cancelButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -1037,12 +1152,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat editorDoneLayout.setBackgroundColor(0x7f000000); editorDoneLayout.updateSelectedCount(0, false); editorDoneLayout.setVisibility(View.GONE); - containerView.addView(editorDoneLayout); - layoutParams = (FrameLayout.LayoutParams) editorDoneLayout.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = AndroidUtilities.dp(48); - layoutParams.gravity = Gravity.BOTTOM; - editorDoneLayout.setLayoutParams(layoutParams); + containerView.addView(editorDoneLayout, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, 48, RelativeLayout.ALIGN_PARENT_BOTTOM)); editorDoneLayout.cancelButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -1057,6 +1167,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat public void onClick(View view) { if (currentEditMode == 1) { photoCropView.cancelAnimationRunnable(); + if (imageMoveAnimation != null) { + return; + } } applyCurrentEditMode(); switchToEditMode(0); @@ -1067,29 +1180,25 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat gestureDetector.setOnDoubleTapListener(this); centerImage.setParentView(containerView); + centerImage.setCrossfadeAlpha((byte) 2); + centerImage.setInvalidateAll(true); leftImage.setParentView(containerView); + leftImage.setCrossfadeAlpha((byte) 2); + leftImage.setInvalidateAll(true); rightImage.setParentView(containerView); + rightImage.setCrossfadeAlpha((byte) 2); + rightImage.setInvalidateAll(true); + + WindowManager manager = (WindowManager) ApplicationLoader.applicationContext.getSystemService(Activity.WINDOW_SERVICE); + int rotation = manager.getDefaultDisplay().getRotation(); checkImageView = new CheckBox(containerView.getContext(), R.drawable.selectphoto_large); checkImageView.setDrawBackground(true); checkImageView.setSize(45); checkImageView.setCheckOffset(AndroidUtilities.dp(1)); checkImageView.setColor(0xff3ccaef); - containerView.addView(checkImageView); checkImageView.setVisibility(View.GONE); - layoutParams = (FrameLayout.LayoutParams) checkImageView.getLayoutParams(); - layoutParams.width = AndroidUtilities.dp(45); - layoutParams.height = AndroidUtilities.dp(45); - layoutParams.gravity = Gravity.RIGHT; - layoutParams.rightMargin = AndroidUtilities.dp(10); - WindowManager manager = (WindowManager) ApplicationLoader.applicationContext.getSystemService(Activity.WINDOW_SERVICE); - int rotation = manager.getDefaultDisplay().getRotation(); - if (rotation == Surface.ROTATION_270 || rotation == Surface.ROTATION_90) { - layoutParams.topMargin = AndroidUtilities.dp(58); - } else { - layoutParams.topMargin = AndroidUtilities.dp(68); - } - checkImageView.setLayoutParams(layoutParams); + containerView.addView(checkImageView, LayoutHelper.createRelative(45, 45, 0, rotation == Surface.ROTATION_270 || rotation == Surface.ROTATION_90 ? 58 : 68, 10, 0, RelativeLayout.ALIGN_PARENT_RIGHT)); checkImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -1100,6 +1209,220 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } } }); + + captionEditText = new PhotoViewerCaptionEnterView(parentActivity, windowView, containerView); + captionEditText.setId(1000); + captionEditText.setDelegate(new PhotoViewerCaptionEnterView.PhotoViewerCaptionEnterViewDelegate() { + @Override + public void onCaptionEnter() { + closeCaptionEnter(true); + } + + @Override + public void onTextChanged(CharSequence text, boolean bigChange) { + if (mentionsAdapter != null && captionEditText != null && parentChatActivity != null && text != null) { + mentionsAdapter.searchUsernameOrHashtag(text.toString(), captionEditText.getCursorPosition(), parentChatActivity.messages); + } + } + + @Override + public void onWindowSizeChanged(int size) { + int height = AndroidUtilities.dp(36 * Math.min(3, mentionsAdapter.getCount()) + (mentionsAdapter.getCount() > 3 ? 18 : 0)); + if (size - AndroidUtilities.getCurrentActionBarHeight() * 2 < height) { + allowMentions = false; + if (mentionListView != null && mentionListView.getVisibility() == View.VISIBLE) { + mentionListView.clearAnimation(); + mentionListView.setVisibility(View.INVISIBLE); + } + } else { + allowMentions = true; + if (mentionListView != null && mentionListView.getVisibility() == View.INVISIBLE) { + mentionListView.clearAnimation(); + mentionListView.setVisibility(View.VISIBLE); + } + } + } + }); + containerView.addView(captionEditText, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 0, -400, RelativeLayout.ALIGN_PARENT_BOTTOM)); + + mentionListView = new ListView(parentActivity); + mentionListView.setBackgroundColor(0x7f000000); + mentionListView.setVisibility(View.GONE); + mentionListView.setClipToPadding(true); + mentionListView.setDividerHeight(0); + mentionListView.setDivider(null); + if (Build.VERSION.SDK_INT > 8) { + mentionListView.setOverScrollMode(ListView.OVER_SCROLL_NEVER); + } + containerView.addView(mentionListView, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, 110, 0, -110, 0, 0, RelativeLayout.ALIGN_TOP, 1000)); + + mentionListView.setAdapter(mentionsAdapter = new MentionsAdapter(parentActivity, true, new MentionsAdapter.MentionsAdapterDelegate() { + @Override + public void needChangePanelVisibility(boolean show) { + if (show) { + RelativeLayout.LayoutParams layoutParams3 = (RelativeLayout.LayoutParams) mentionListView.getLayoutParams(); + int height = 36 * Math.min(3, mentionsAdapter.getCount()) + (mentionsAdapter.getCount() > 3 ? 18 : 0); + layoutParams3.height = AndroidUtilities.dp(height); + layoutParams3.topMargin = -AndroidUtilities.dp(height); + mentionListView.setLayoutParams(layoutParams3); + + if (mentionListAnimation != null) { + mentionListAnimation.cancel(); + mentionListAnimation = null; + } + + if (mentionListView.getVisibility() == View.VISIBLE) { + ViewProxy.setAlpha(mentionListView, 1.0f); + return; + } + if (allowMentions) { + mentionListView.setVisibility(View.VISIBLE); + mentionListAnimation = new AnimatorSetProxy(); + mentionListAnimation.playTogether( + ObjectAnimatorProxy.ofFloat(mentionListView, "alpha", 0.0f, 1.0f) + ); + mentionListAnimation.addListener(new AnimatorListenerAdapterProxy() { + @Override + public void onAnimationEnd(Object animation) { + if (mentionListAnimation != null && mentionListAnimation.equals(animation)) { + mentionListView.clearAnimation(); + mentionListAnimation = null; + } + } + }); + mentionListAnimation.setDuration(200); + mentionListAnimation.start(); + } else { + ViewProxy.setAlpha(mentionListView, 1.0f); + mentionListView.clearAnimation(); + mentionListView.setVisibility(View.INVISIBLE); + } + } else { + if (mentionListAnimation != null) { + mentionListAnimation.cancel(); + mentionListAnimation = null; + } + + if (mentionListView.getVisibility() == View.GONE) { + return; + } + if (allowMentions) { + mentionListAnimation = new AnimatorSetProxy(); + mentionListAnimation.playTogether( + ObjectAnimatorProxy.ofFloat(mentionListView, "alpha", 0.0f) + ); + mentionListAnimation.addListener(new AnimatorListenerAdapterProxy() { + @Override + public void onAnimationEnd(Object animation) { + if (mentionListAnimation != null && mentionListAnimation.equals(animation)) { + mentionListView.clearAnimation(); + mentionListView.setVisibility(View.GONE); + mentionListAnimation = null; + } + } + }); + mentionListAnimation.setDuration(200); + mentionListAnimation.start(); + } else { + mentionListView.clearAnimation(); + mentionListView.setVisibility(View.GONE); + } + } + } + })); + + mentionListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + Object object = mentionsAdapter.getItem(position); + int start = mentionsAdapter.getResultStartPosition(); + int len = mentionsAdapter.getResultLength(); + if (object instanceof TLRPC.User) { + TLRPC.User user = (TLRPC.User) object; + if (user != null) { + captionEditText.replaceWithText(start, len, "@" + user.username + " "); + } + } else if (object instanceof String) { + captionEditText.replaceWithText(start, len, object + " "); + } + } + }); + + mentionListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { + @Override + public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { + Object object = mentionsAdapter.getItem(position); + if (object instanceof String) { + AlertDialog.Builder builder = new AlertDialog.Builder(parentActivity); + builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); + builder.setMessage(LocaleController.getString("ClearSearch", R.string.ClearSearch)); + builder.setPositiveButton(LocaleController.getString("ClearButton", R.string.ClearButton).toUpperCase(), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + mentionsAdapter.clearRecentHashtags(); + } + }); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + showAlertDialog(builder); + return true; + } + return false; + } + }); + } + + private void updateCaptionTextForCurrentPhoto(Object object) { + CharSequence caption = null; + if (object instanceof MediaController.PhotoEntry) { + caption = ((MediaController.PhotoEntry) object).caption; + } else if (object instanceof MediaController.SearchImage) { + caption = ((MediaController.SearchImage) object).caption; + } + if (caption == null || caption.length() == 0) { + captionEditText.setFieldText(""); + } else { + captionEditText.setFieldText(caption); + } + } + + private void closeCaptionEnter(boolean apply) { + Object object = imagesArrLocals.get(currentIndex); + if (apply) { + if (object instanceof MediaController.PhotoEntry) { + ((MediaController.PhotoEntry) object).caption = captionEditText.getFieldCharSequence(); + } else if (object instanceof MediaController.SearchImage) { + ((MediaController.SearchImage) object).caption = captionEditText.getFieldCharSequence(); + } + + if (captionEditText.getFieldCharSequence().length() != 0 && !placeProvider.isPhotoChecked(currentIndex)) { + placeProvider.setPhotoChecked(currentIndex); + checkImageView.setChecked(placeProvider.isPhotoChecked(currentIndex), true); + updateSelectedCount(); + } + } + cropItem.setVisibility(View.VISIBLE); + captionItem.setVisibility(View.VISIBLE); + if (Build.VERSION.SDK_INT >= 16) { + tuneItem.setVisibility(View.VISIBLE); + } + if (sendPhotoType == 0) { + checkImageView.setVisibility(View.VISIBLE); + } + captionDoneItem.setVisibility(View.GONE); + pickerView.setVisibility(View.VISIBLE); + + if (lastTitle != null) { + actionBar.setTitle(lastTitle); + lastTitle = null; + } + + updateCaptionTextForCurrentPhoto(object); + setCurrentCaption(captionEditText.getFieldCharSequence()); + if (captionEditText.isEmojiPopupShowing()) { + captionEditText.hideEmojiPopup(); + } else { + captionEditText.closeKeyboard(); + } } private void showAlertDialog(AlertDialog.Builder builder) { @@ -1179,7 +1502,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } private void switchToEditMode(final int mode) { - if (currentEditMode == mode || centerImage.getBitmap() == null || imageMoveAnimation != null || radialProgressViews[0].backgroundState != -1) { + if (currentEditMode == mode || centerImage.getBitmap() == null || changeModeAnimation != null || imageMoveAnimation != null || radialProgressViews[0].backgroundState != -1) { return; } if (mode == 0) { @@ -1255,6 +1578,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat ArrayList arrayList = new ArrayList<>(); arrayList.add(ObjectAnimatorProxy.ofFloat(pickerView, "translationY", 0)); arrayList.add(ObjectAnimatorProxy.ofFloat(actionBar, "translationY", 0)); + if (needCaptionLayout) { + arrayList.add(ObjectAnimatorProxy.ofFloat(captionTextView, "translationY", 0)); + } if (sendPhotoType == 0) { arrayList.add(ObjectAnimatorProxy.ofFloat(checkImageView, "alpha", 1)); } @@ -1265,6 +1591,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat public void onAnimationStart(Object animation) { pickerView.setVisibility(View.VISIBLE); actionBar.setVisibility(View.VISIBLE); + if (needCaptionLayout) { + captionTextView.setVisibility(captionTextView.getTag() != null ? View.VISIBLE : View.INVISIBLE); + } if (sendPhotoType == 0) { checkImageView.setVisibility(View.VISIBLE); } @@ -1274,6 +1603,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat public void onAnimationEnd(Object animation) { pickerView.clearAnimation(); actionBar.clearAnimation(); + if (needCaptionLayout) { + captionTextView.clearAnimation(); + } if (sendPhotoType == 0) { checkImageView.clearAnimation(); } @@ -1287,12 +1619,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (photoCropView == null) { photoCropView = new PhotoCropView(parentActivity); photoCropView.setVisibility(View.GONE); - containerView.addView(photoCropView); - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) photoCropView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.bottomMargin = AndroidUtilities.dp(48); - photoCropView.setLayoutParams(layoutParams); + containerView.addView(photoCropView, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0, 0, 0, 48)); photoCropView.setDelegate(new PhotoCropView.PhotoCropViewDelegate() { @Override public void needMoveImageTo(float x, float y, float s, boolean animated) { @@ -1309,22 +1636,30 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } editorDoneLayout.doneButtonTextView.setText(LocaleController.getString("Crop", R.string.Crop)); - AnimatorSetProxy animatorSet = new AnimatorSetProxy(); + changeModeAnimation = new AnimatorSetProxy(); ArrayList arrayList = new ArrayList<>(); - arrayList.add(ObjectAnimatorProxy.ofFloat(pickerView, "translationY", 0, AndroidUtilities.dp(48))); + arrayList.add(ObjectAnimatorProxy.ofFloat(pickerView, "translationY", 0, AndroidUtilities.dp(96))); arrayList.add(ObjectAnimatorProxy.ofFloat(actionBar, "translationY", 0, -actionBar.getHeight())); + if (needCaptionLayout) { + arrayList.add(ObjectAnimatorProxy.ofFloat(captionTextView, "translationY", 0, AndroidUtilities.dp(96))); + } if (sendPhotoType == 0) { arrayList.add(ObjectAnimatorProxy.ofFloat(checkImageView, "alpha", 1, 0)); } - animatorSet.playTogether(arrayList); - animatorSet.setDuration(200); - animatorSet.addListener(new AnimatorListenerAdapterProxy() { + changeModeAnimation.playTogether(arrayList); + changeModeAnimation.setDuration(200); + changeModeAnimation.addListener(new AnimatorListenerAdapterProxy() { @Override public void onAnimationEnd(Object animation) { + changeModeAnimation = null; pickerView.clearAnimation(); actionBar.clearAnimation(); pickerView.setVisibility(View.GONE); actionBar.setVisibility(View.GONE); + if (needCaptionLayout) { + captionTextView.clearAnimation(); + captionTextView.setVisibility(View.INVISIBLE); + } if (sendPhotoType == 0) { checkImageView.clearAnimation(); checkImageView.setVisibility(View.GONE); @@ -1383,15 +1718,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat imageMoveAnimation.start(); } }); - animatorSet.start(); + changeModeAnimation.start(); } else if (mode == 2) { if (photoFilterView == null) { photoFilterView = new PhotoFilterView(parentActivity, centerImage.getBitmap(), centerImage.getOrientation()); - containerView.addView(photoFilterView); - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) photoFilterView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; - photoFilterView.setLayoutParams(layoutParams); + containerView.addView(photoFilterView, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); photoFilterView.getDoneTextView().setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -1426,22 +1757,30 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat ViewProxy.setTranslationY(photoFilterView.getToolsView(), AndroidUtilities.dp(126)); } - AnimatorSetProxy animatorSet = new AnimatorSetProxy(); + changeModeAnimation = new AnimatorSetProxy(); ArrayList arrayList = new ArrayList<>(); - arrayList.add(ObjectAnimatorProxy.ofFloat(pickerView, "translationY", 0, AndroidUtilities.dp(48))); + arrayList.add(ObjectAnimatorProxy.ofFloat(pickerView, "translationY", 0, AndroidUtilities.dp(96))); arrayList.add(ObjectAnimatorProxy.ofFloat(actionBar, "translationY", 0, -actionBar.getHeight())); + if (needCaptionLayout) { + arrayList.add(ObjectAnimatorProxy.ofFloat(captionTextView, "translationY", 0, AndroidUtilities.dp(96))); + } if (sendPhotoType == 0) { arrayList.add(ObjectAnimatorProxy.ofFloat(checkImageView, "alpha", 1, 0)); } - animatorSet.playTogether(arrayList); - animatorSet.setDuration(200); - animatorSet.addListener(new AnimatorListenerAdapterProxy() { + changeModeAnimation.playTogether(arrayList); + changeModeAnimation.setDuration(200); + changeModeAnimation.addListener(new AnimatorListenerAdapterProxy() { @Override public void onAnimationEnd(Object animation) { + changeModeAnimation = null; pickerView.clearAnimation(); actionBar.clearAnimation(); pickerView.setVisibility(View.GONE); actionBar.setVisibility(View.GONE); + if (needCaptionLayout) { + captionTextView.clearAnimation(); + captionTextView.setVisibility(View.INVISIBLE); + } if (sendPhotoType == 0) { checkImageView.clearAnimation(); checkImageView.setVisibility(View.GONE); @@ -1497,28 +1836,17 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat imageMoveAnimation.start(); } }); - animatorSet.start(); + changeModeAnimation.start(); } - /*Bundle args = new Bundle(); - Bitmap bitmap = centerImage.getBitmap(); - String key = centerImage.getKey(); - if (bitmap == null) { - args.putString("photoPath", currentPathObject); - } - MediaController.PhotoEntry object = - args.putInt("id", object.imageId); - args.putBoolean("freeformCrop", true); - args.putBoolean("onlyCrop", true); - PhotoEditorActivity fragment = new PhotoEditorActivity(args, bitmap, key); - fragment.setDelegate((PhotoCropActivity.PhotoEditActivityDelegate) placeProvider); - ((LaunchActivity) parentActivity).presentFragment(fragment, false, true); - closePhoto(false);*/ } private void toggleCheckImageView(boolean show) { AnimatorSetProxy animatorSet = new AnimatorSetProxy(); ArrayList arrayList = new ArrayList<>(); arrayList.add(ObjectAnimatorProxy.ofFloat(pickerView, "alpha", show ? 1.0f : 0.0f)); + if (needCaptionLayout) { + arrayList.add(ObjectAnimatorProxy.ofFloat(captionTextView, "alpha", show ? 1.0f : 0.0f)); + } if (sendPhotoType == 0) { arrayList.add(ObjectAnimatorProxy.ofFloat(checkImageView, "alpha", show ? 1.0f : 0.0f)); } @@ -1532,6 +1860,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat actionBar.setVisibility(View.VISIBLE); if (canShowBottom) { bottomLayout.setVisibility(View.VISIBLE); + if (captionTextView.getTag() != null) { + captionTextView.setVisibility(View.VISIBLE); + } } } isActionBarVisible = show; @@ -1539,11 +1870,14 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat bottomLayout.setEnabled(show); if (animated) { + ArrayList arrayList = new ArrayList<>(); + arrayList.add(ObjectAnimatorProxy.ofFloat(actionBar, "alpha", show ? 1.0f : 0.0f)); + arrayList.add(ObjectAnimatorProxy.ofFloat(bottomLayout, "alpha", show ? 1.0f : 0.0f)); + if (captionTextView.getTag() != null) { + arrayList.add(ObjectAnimatorProxy.ofFloat(captionTextView, "alpha", show ? 1.0f : 0.0f)); + } currentActionBarAnimation = new AnimatorSetProxy(); - currentActionBarAnimation.playTogether( - ObjectAnimatorProxy.ofFloat(actionBar, "alpha", show ? 1.0f : 0.0f), - ObjectAnimatorProxy.ofFloat(bottomLayout, "alpha", show ? 1.0f : 0.0f) - ); + currentActionBarAnimation.playTogether(arrayList); if (!show) { currentActionBarAnimation.addListener(new AnimatorListenerAdapterProxy() { @Override @@ -1553,6 +1887,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (canShowBottom) { bottomLayout.clearAnimation(); bottomLayout.setVisibility(View.GONE); + if (captionTextView.getTag() != null) { + captionTextView.clearAnimation(); + captionTextView.setVisibility(View.INVISIBLE); + } } currentActionBarAnimation = null; } @@ -1565,11 +1903,18 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } else { ViewProxy.setAlpha(actionBar, show ? 1.0f : 0.0f); ViewProxy.setAlpha(bottomLayout, show ? 1.0f : 0.0f); + if (captionTextView.getTag() != null) { + ViewProxy.setAlpha(captionTextView, show ? 1.0f : 0.0f); + } if (!show) { actionBar.setVisibility(View.GONE); if (canShowBottom) { bottomLayout.clearAnimation(); bottomLayout.setVisibility(View.GONE); + if (captionTextView.getTag() != null) { + captionTextView.clearAnimation(); + captionTextView.setVisibility(View.INVISIBLE); + } } } } @@ -1756,6 +2101,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat loadingMoreImages = false; cacheEndReached = false; opennedFromMedia = false; + needCaptionLayout = false; canShowBottom = true; imagesArr.clear(); imagesArrLocations.clear(); @@ -1766,6 +2112,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat imagesArrTemp.clear(); imagesByIdsTemp.clear(); currentUserAvatarLocation = null; + containerView.setPadding(0, 0, 0, 0); currentThumb = object != null ? object.thumb : null; menuItem.setVisibility(View.VISIBLE); bottomLayout.setVisibility(View.VISIBLE); @@ -1782,7 +2129,15 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat pickerView.setVisibility(View.GONE); cropItem.setVisibility(View.GONE); tuneItem.setVisibility(View.GONE); + captionItem.setVisibility(View.GONE); + captionDoneItem.setVisibility(View.GONE); + captionEditText.clearAnimation(); + captionEditText.setVisibility(View.GONE); + mentionListView.setVisibility(View.GONE); editorDoneLayout.setVisibility(View.GONE); + captionTextView.setTag(null); + captionTextView.clearAnimation(); + captionTextView.setVisibility(View.INVISIBLE); if (photoCropView != null) { photoCropView.clearAnimation(); photoCropView.setVisibility(View.GONE); @@ -1874,6 +2229,19 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat canShowBottom = false; Object obj = imagesArrLocals.get(index); cropItem.setVisibility(obj instanceof MediaController.PhotoEntry || obj instanceof MediaController.SearchImage && ((MediaController.SearchImage) obj).type == 0 ? View.VISIBLE : View.GONE); + if (parentChatActivity != null && parentChatActivity.currentEncryptedChat == null) { + mentionsAdapter.setChatInfo(parentChatActivity.info); + mentionsAdapter.setNeedUsernames(parentChatActivity.currentChat != null); + captionItem.setVisibility(cropItem.getVisibility()); + captionEditText.setVisibility(cropItem.getVisibility()); + needCaptionLayout = captionItem.getVisibility() == View.VISIBLE; + if (needCaptionLayout) { + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) captionEditText.getLayoutParams(); + layoutParams.bottomMargin = -AndroidUtilities.dp(400); + captionEditText.setLayoutParams(layoutParams); + captionEditText.onCreate(); + } + } if (Build.VERSION.SDK_INT >= 16) { tuneItem.setVisibility(cropItem.getVisibility()); } @@ -1927,10 +2295,12 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat long date = (long) currentMessageObject.messageOwner.date * 1000; String dateString = LocaleController.formatString("formatDateAtTime", R.string.formatDateAtTime, LocaleController.formatterYear.format(new Date(date)), LocaleController.formatterDay.format(new Date(date))); if (currentFileNames[0] != null && currentFileNames[0].endsWith("mp4")) { - dateTextView.setText(String.format("%s (%s)", dateString, Utilities.formatFileSize(currentMessageObject.messageOwner.media.video.size))); + dateTextView.setText(String.format("%s (%s)", dateString, AndroidUtilities.formatFileSize(currentMessageObject.messageOwner.media.video.size))); } else { dateTextView.setText(dateString); } + CharSequence caption = currentMessageObject.caption; + setCurrentCaption(caption); if (totalImagesCount != 0 && !needSearchImageInArr) { if (opennedFromMedia) { @@ -1983,11 +2353,14 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat return; } boolean fromCamera = false; + CharSequence caption = null; if (object instanceof MediaController.PhotoEntry) { currentPathObject = ((MediaController.PhotoEntry) object).path; fromCamera = ((MediaController.PhotoEntry) object).bucketId == 0 && ((MediaController.PhotoEntry) object).dateTaken == 0 && imagesArrLocals.size() == 1; + caption = ((MediaController.PhotoEntry) object).caption; } else if (object instanceof MediaController.SearchImage) { currentPathObject = ((MediaController.SearchImage) object).imageUrl; + caption = ((MediaController.SearchImage) object).caption; } if (fromCamera) { actionBar.setTitle(LocaleController.getString("AttachPhoto", R.string.AttachPhoto)); @@ -1997,6 +2370,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (sendPhotoType == 0) { checkImageView.setChecked(placeProvider.isPhotoChecked(currentIndex), false); } + + setCurrentCaption(caption); + updateCaptionTextForCurrentPhoto(object); } @@ -2026,6 +2402,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat animateToScale = 1; animationStartTime = 0; imageMoveAnimation = null; + changeModeAnimation = null; pinchStartDistance = 0; pinchStartScale = 1; @@ -2086,6 +2463,34 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat createGifForCurrentImage(); } + private void setCurrentCaption(final CharSequence caption) { + if (caption != null && caption.length() > 0) { + captionTextView = captionTextViewOld; + captionTextViewOld = captionTextViewNew; + captionTextViewNew = captionTextView; + + captionItem.setIcon(R.drawable.photo_text2); + CharSequence oldText = captionTextView.getText(); + captionTextView.setTag(caption); + captionTextView.setText(caption); + ViewProxy.setAlpha(captionTextView, bottomLayout.getVisibility() == View.VISIBLE || pickerView.getVisibility() == View.VISIBLE ? 1.0f : 0.0f); + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + captionTextViewOld.setTag(null); + captionTextViewOld.clearAnimation(); + captionTextViewOld.setVisibility(View.INVISIBLE); + captionTextViewNew.setVisibility(bottomLayout.getVisibility() == View.VISIBLE || pickerView.getVisibility() == View.VISIBLE ? View.VISIBLE : View.INVISIBLE); + } + }); + } else { + captionItem.setIcon(R.drawable.photo_text); + captionTextView.setTag(null); + captionTextView.clearAnimation(); + captionTextView.setVisibility(View.INVISIBLE); + } + } + private void createGifForCurrentImage() { if (gifDrawable != null) { gifDrawable.recycle(); @@ -2202,7 +2607,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat imageSize = ((MediaController.SearchImage) object).size; } } - imageReceiver.setImage(path, String.format(Locale.US, "%d_%d", size, size), placeHolder != null ? new BitmapDrawable(null, placeHolder) : null, imageSize); + imageReceiver.setImage(path, String.format(Locale.US, "%d_%d", size, size), placeHolder != null ? new BitmapDrawable(null, placeHolder) : null, null, imageSize); } else { imageReceiver.setImageBitmap((Bitmap) null); } @@ -2228,7 +2633,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat placeHolder = currentThumb; } TLRPC.PhotoSize thumbLocation = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, 100); - imageReceiver.setImage(null, null, null, placeHolder != null ? new BitmapDrawable(null, placeHolder) : null, thumbLocation.location, "b", 0, true); + imageReceiver.setImage(null, null, null, placeHolder != null ? new BitmapDrawable(null, placeHolder) : null, thumbLocation.location, "b", 0, null, true); } else { imageReceiver.setImageBitmap(parentActivity.getResources().getDrawable(R.drawable.photoview_placeholder)); } @@ -2242,7 +2647,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat size[0] = -1; } TLRPC.PhotoSize thumbLocation = messageObject != null ? FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, 100) : null; - imageReceiver.setImage(fileLocation, null, null, placeHolder != null ? new BitmapDrawable(null, placeHolder) : null, thumbLocation != null ? thumbLocation.location : null, "b", size[0], avatarsUserId != 0); + imageReceiver.setImage(fileLocation, null, null, placeHolder != null ? new BitmapDrawable(null, placeHolder) : null, thumbLocation != null ? thumbLocation.location : null, "b", size[0], null, avatarsUserId != 0); } } else { imageReceiver.setNeedsQualityThumb(false); @@ -2269,23 +2674,23 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } public void openPhoto(final MessageObject messageObject, final PhotoViewerProvider provider) { - openPhoto(messageObject, null, null, null, 0, provider); + openPhoto(messageObject, null, null, null, 0, provider, null); } public void openPhoto(final TLRPC.FileLocation fileLocation, final PhotoViewerProvider provider) { - openPhoto(null, fileLocation, null, null, 0, provider); + openPhoto(null, fileLocation, null, null, 0, provider, null); } public void openPhoto(final ArrayList messages, final int index, final PhotoViewerProvider provider) { - openPhoto(messages.get(index), null, messages, null, index, provider); + openPhoto(messages.get(index), null, messages, null, index, provider, null); } - public void openPhotoForSelect(final ArrayList photos, final int index, int type, final PhotoViewerProvider provider) { + public void openPhotoForSelect(final ArrayList photos, final int index, int type, final PhotoViewerProvider provider, ChatActivity chatActivity) { sendPhotoType = type; if (pickerView != null) { pickerView.doneButtonTextView.setText(sendPhotoType == 1 ? LocaleController.getString("Set", R.string.Set).toUpperCase() : LocaleController.getString("Send", R.string.Send).toUpperCase()); } - openPhoto(null, null, null, photos, index, provider); + openPhoto(null, null, null, photos, index, provider, chatActivity); } private boolean checkAnimation() { @@ -2301,7 +2706,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat return animationInProgress != 0; } - public void openPhoto(final MessageObject messageObject, final TLRPC.FileLocation fileLocation, final ArrayList messages, final ArrayList photos, final int index, final PhotoViewerProvider provider) { + public void openPhoto(final MessageObject messageObject, final TLRPC.FileLocation fileLocation, final ArrayList messages, final ArrayList photos, final int index, final PhotoViewerProvider provider, ChatActivity chatActivity) { if (parentActivity == null || isVisible || provider == null && checkAnimation() || messageObject == null && fileLocation == null && messages == null && photos == null) { return; } @@ -2311,21 +2716,37 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat return; } - try { - WindowManager wm = (WindowManager) parentActivity.getSystemService(Context.WINDOW_SERVICE); - wm.removeView(windowView); - } catch (Exception e) { - //don't promt + WindowManager wm = (WindowManager) parentActivity.getSystemService(Context.WINDOW_SERVICE); + if (windowView.attachedToWindow) { + try { + wm.removeView(windowView); + } catch (Exception e) { + //don't promt + } } - WindowManager wm = (WindowManager) parentActivity.getSystemService(Context.WINDOW_SERVICE); try { + if (photos != null) { + windowLayoutParams.type = WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; + windowLayoutParams.flags = 0; + windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN; + windowView.setFocusable(true); + containerView.setFocusable(true); + } else { + windowLayoutParams.type = WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; + windowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED; + windowView.setFocusable(false); + containerView.setFocusable(false); + } wm.addView(windowView, windowLayoutParams); } catch (Exception e) { FileLog.e("tmessages", e); return; } + parentChatActivity = chatActivity; + actionBar.setTitle(LocaleController.formatString("Of", R.string.Of, 1, 1)); NotificationCenter.getInstance().addObserver(this, NotificationCenter.FileDidFailedLoad); NotificationCenter.getInstance().addObserver(this, NotificationCenter.FileDidLoaded); @@ -2333,6 +2754,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat NotificationCenter.getInstance().addObserver(this, NotificationCenter.mediaCountDidLoaded); NotificationCenter.getInstance().addObserver(this, NotificationCenter.mediaDidLoaded); NotificationCenter.getInstance().addObserver(this, NotificationCenter.userPhotosLoaded); + NotificationCenter.getInstance().addObserver(this, NotificationCenter.emojiDidLoaded); placeProvider = provider; @@ -2341,7 +2763,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } isVisible = true; - backgroundDrawable.setAlpha(255); toggleActionBar(true, false); if (object != null) { @@ -2349,8 +2770,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat animationInProgress = 1; onPhotoShow(messageObject, fileLocation, messages, photos, index, object); - AndroidUtilities.lockOrientation(parentActivity); - final Rect drawRegion = object.imageReceiver.getDrawRegion(); int orientation = object.imageReceiver.getOrientation(); @@ -2372,96 +2791,117 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat layoutParams.height = drawRegion.bottom - drawRegion.top; animatingImageView.setLayoutParams(layoutParams); - containerView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + float scaleX = (float) AndroidUtilities.displaySize.x / layoutParams.width; + float scaleY = (float) (AndroidUtilities.displaySize.y - AndroidUtilities.statusBarHeight) / layoutParams.height; + float scale = scaleX > scaleY ? scaleY : scaleX; + float width = layoutParams.width * scale; + float height = layoutParams.height * scale; + float xPos = (AndroidUtilities.displaySize.x - width) / 2.0f; + float yPos = (AndroidUtilities.displaySize.y - AndroidUtilities.statusBarHeight - height) / 2.0f; + int clipHorizontal = Math.abs(drawRegion.left - object.imageReceiver.getImageX()); + int clipVertical = Math.abs(drawRegion.top - object.imageReceiver.getImageY()); + + int coords2[] = new int[2]; + object.parentView.getLocationInWindow(coords2); + int clipTop = coords2[1] - AndroidUtilities.statusBarHeight - (object.viewY + drawRegion.top); + if (clipTop < 0) { + clipTop = 0; + } + int clipBottom = (object.viewY + drawRegion.top + layoutParams.height) - (coords2[1] + object.parentView.getHeight() - AndroidUtilities.statusBarHeight); + if (clipBottom < 0) { + clipBottom = 0; + } + clipTop = Math.max(clipTop, clipVertical); + clipBottom = Math.max(clipBottom, clipVertical); + + animationValues[0][0] = ViewProxy.getScaleX(animatingImageView); + animationValues[0][1] = ViewProxy.getScaleY(animatingImageView); + animationValues[0][2] = ViewProxy.getTranslationX(animatingImageView); + animationValues[0][3] = ViewProxy.getTranslationY(animatingImageView); + animationValues[0][4] = clipHorizontal; + animationValues[0][5] = clipTop; + animationValues[0][6] = clipBottom; + animationValues[0][7] = animatingImageView.getRadius(); + + animationValues[1][0] = scale; + animationValues[1][1] = scale; + animationValues[1][2] = xPos; + animationValues[1][3] = yPos; + animationValues[1][4] = 0; + animationValues[1][5] = 0; + animationValues[1][6] = 0; + animationValues[1][7] = 0; + + animatingImageView.setAnimationProgress(0); + backgroundDrawable.setAlpha(0); + ViewProxy.setAlpha(containerView, 0); + + final AnimatorSetProxy animatorSet = new AnimatorSetProxy(); + animatorSet.playTogether( + ObjectAnimatorProxy.ofFloat(animatingImageView, "animationProgress", 0.0f, 1.0f), + ObjectAnimatorProxy.ofInt(backgroundDrawable, "alpha", 0, 255), + ObjectAnimatorProxy.ofFloat(containerView, "alpha", 0.0f, 1.0f) + ); + + animationEndRunnable = new Runnable() { @Override - public boolean onPreDraw() { - containerView.getViewTreeObserver().removeOnPreDrawListener(this); - - float scaleX = (float) AndroidUtilities.displaySize.x / layoutParams.width; - float scaleY = (float) (AndroidUtilities.displaySize.y - AndroidUtilities.statusBarHeight) / layoutParams.height; - float scale = scaleX > scaleY ? scaleY : scaleX; - float width = layoutParams.width * scale; - float height = layoutParams.height * scale; - float xPos = (AndroidUtilities.displaySize.x - width) / 2.0f; - float yPos = (AndroidUtilities.displaySize.y - AndroidUtilities.statusBarHeight - height) / 2.0f; - int clipHorizontal = Math.abs(drawRegion.left - object.imageReceiver.getImageX()); - int clipVertical = Math.abs(drawRegion.top - object.imageReceiver.getImageY()); - - int coords2[] = new int[2]; - object.parentView.getLocationInWindow(coords2); - int clipTop = coords2[1] - AndroidUtilities.statusBarHeight - (object.viewY + drawRegion.top); - if (clipTop < 0) { - clipTop = 0; + public void run() { + if (containerView == null) { + return; } - int clipBottom = (object.viewY + drawRegion.top + layoutParams.height) - (coords2[1] + object.parentView.getHeight() - AndroidUtilities.statusBarHeight); - if (clipBottom < 0) { - clipBottom = 0; + animationInProgress = 0; + transitionAnimationStartTime = 0; + setImages(); + containerView.invalidate(); + animatingImageView.setVisibility(View.GONE); + if (showAfterAnimation != null) { + showAfterAnimation.imageReceiver.setVisible(true, true); } - clipTop = Math.max(clipTop, clipVertical); - clipBottom = Math.max(clipBottom, clipVertical); + if (hideAfterAnimation != null) { + hideAfterAnimation.imageReceiver.setVisible(false, true); + } + } + }; - AnimatorSetProxy animatorSet = new AnimatorSetProxy(); - animatorSet.playTogether( - ObjectAnimatorProxy.ofFloat(animatingImageView, "scaleX", scale), - ObjectAnimatorProxy.ofFloat(animatingImageView, "scaleY", scale), - ObjectAnimatorProxy.ofFloat(animatingImageView, "translationX", xPos), - ObjectAnimatorProxy.ofFloat(animatingImageView, "translationY", yPos), - ObjectAnimatorProxy.ofInt(backgroundDrawable, "alpha", 0, 255), - ObjectAnimatorProxy.ofInt(animatingImageView, "clipHorizontal", clipHorizontal, 0), - ObjectAnimatorProxy.ofInt(animatingImageView, "clipTop", clipTop, 0), - ObjectAnimatorProxy.ofInt(animatingImageView, "clipBottom", clipBottom, 0), - ObjectAnimatorProxy.ofInt(animatingImageView, "radius", 0), - ObjectAnimatorProxy.ofFloat(containerView, "alpha", 0.0f, 1.0f) - ); - - animationEndRunnable = new Runnable() { + animatorSet.setDuration(200); + animatorSet.addListener(new AnimatorListenerAdapterProxy() { + @Override + public void onAnimationEnd(Object animation) { + AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { - animationInProgress = 0; - setImages(); - transitionAnimationStartTime = 0; - containerView.invalidate(); - animatingImageView.setVisibility(View.GONE); - AndroidUtilities.unlockOrientation(parentActivity); - if (showAfterAnimation != null) { - showAfterAnimation.imageReceiver.setVisible(true, true); - } - if (hideAfterAnimation != null) { - hideAfterAnimation.imageReceiver.setVisible(false, true); - } - } - }; - - animatorSet.setDuration(200); - animatorSet.addListener(new AnimatorListenerAdapterProxy() { - @Override - public void onAnimationEnd(Object animation) { + NotificationCenter.getInstance().setAnimationInProgress(false); if (animationEndRunnable != null) { animationEndRunnable.run(); animationEndRunnable = null; } } - - @Override - public void onAnimationCancel(Object animation) { - onAnimationEnd(animation); - } }); - transitionAnimationStartTime = System.currentTimeMillis(); - animatorSet.start(); + } - animatingImageView.setOnDrawListener(new ClippingImageView.onDrawListener() { - @Override - public void onDraw() { - disableShowCheck = false; - animatingImageView.setOnDrawListener(null); - object.imageReceiver.setVisible(false, true); - } - }); - return true; + @Override + public void onAnimationCancel(Object animation) { + onAnimationEnd(animation); } }); + transitionAnimationStartTime = System.currentTimeMillis(); + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + NotificationCenter.getInstance().setAnimationInProgress(true); + animatorSet.start(); + } + }); + + backgroundDrawable.drawRunnable = new Runnable() { + @Override + public void run() { + disableShowCheck = false; + object.imageReceiver.setVisible(false, true); + } + }; } else { + backgroundDrawable.setAlpha(255); ViewProxy.setAlpha(containerView, 1.0f); onPhotoShow(messageObject, fileLocation, messages, photos, index, object); } @@ -2500,12 +2940,15 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat return; } + captionEditText.onDestroy(); + parentChatActivity = null; NotificationCenter.getInstance().removeObserver(this, NotificationCenter.FileDidFailedLoad); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.FileDidLoaded); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.FileLoadProgressChanged); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.mediaCountDidLoaded); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.mediaDidLoaded); NotificationCenter.getInstance().removeObserver(this, NotificationCenter.userPhotosLoaded); + NotificationCenter.getInstance().removeObserver(this, NotificationCenter.emojiDidLoaded); ConnectionsManager.getInstance().cancelRpcsForClassGuid(classGuid); isActionBarVisible = false; @@ -2519,8 +2962,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat final PlaceProviderObject object = placeProvider.getPlaceForPhoto(currentMessageObject, currentFileLocation, currentIndex); if (animated) { - AndroidUtilities.lockOrientation(parentActivity); - animationInProgress = 1; int visibility = animatingImageView.getVisibility(); animatingImageView.setVisibility(View.VISIBLE); @@ -2576,16 +3017,27 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat clipTop = Math.max(clipTop, clipVertical); clipBottom = Math.max(clipBottom, clipVertical); + animationValues[0][0] = ViewProxy.getScaleX(animatingImageView); + animationValues[0][1] = ViewProxy.getScaleY(animatingImageView); + animationValues[0][2] = ViewProxy.getTranslationX(animatingImageView); + animationValues[0][3] = ViewProxy.getTranslationY(animatingImageView); + animationValues[0][4] = 0; + animationValues[0][5] = 0; + animationValues[0][6] = 0; + animationValues[0][7] = 0; + + animationValues[1][0] = 1; + animationValues[1][1] = 1; + animationValues[1][2] = object.viewX + drawRegion.left; + animationValues[1][3] = object.viewY + drawRegion.top; + animationValues[1][4] = clipHorizontal; + animationValues[1][5] = clipTop; + animationValues[1][6] = clipBottom; + animationValues[1][7] = object.radius; + animatorSet.playTogether( - ObjectAnimatorProxy.ofFloat(animatingImageView, "scaleX", 1), - ObjectAnimatorProxy.ofFloat(animatingImageView, "scaleY", 1), - ObjectAnimatorProxy.ofFloat(animatingImageView, "translationX", object.viewX + drawRegion.left), - ObjectAnimatorProxy.ofFloat(animatingImageView, "translationY", object.viewY + drawRegion.top), + ObjectAnimatorProxy.ofFloat(animatingImageView, "animationProgress", 0.0f, 1.0f), ObjectAnimatorProxy.ofInt(backgroundDrawable, "alpha", 0), - ObjectAnimatorProxy.ofInt(animatingImageView, "clipHorizontal", clipHorizontal), - ObjectAnimatorProxy.ofInt(animatingImageView, "clipTop", clipTop), - ObjectAnimatorProxy.ofInt(animatingImageView, "clipBottom", clipBottom), - ObjectAnimatorProxy.ofInt(animatingImageView, "radius", object.radius), ObjectAnimatorProxy.ofFloat(containerView, "alpha", 0.0f) ); } else { @@ -2600,7 +3052,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat animationEndRunnable = new Runnable() { @Override public void run() { - AndroidUtilities.unlockOrientation(parentActivity); animationInProgress = 0; onPhotoClosed(object); } @@ -2610,10 +3061,15 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat animatorSet.addListener(new AnimatorListenerAdapterProxy() { @Override public void onAnimationEnd(Object animation) { - if (animationEndRunnable != null) { - animationEndRunnable.run(); - animationEndRunnable = null; - } + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + if (animationEndRunnable != null) { + animationEndRunnable.run(); + animationEndRunnable = null; + } + } + }); } @Override @@ -2635,6 +3091,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat animationEndRunnable = new Runnable() { @Override public void run() { + if (containerView == null) { + return; + } animationInProgress = 0; onPhotoClosed(object); ViewProxy.setScaleX(containerView, 1.0f); @@ -2670,6 +3129,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } catch (Exception e) { FileLog.e("tmessages", e); } + if (captionEditText != null) { + captionEditText.onDestroy(); + } Instance = null; } @@ -2716,6 +3178,24 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } } + private void redraw(final int count) { + if (count < 6) { + if (containerView != null) { + containerView.invalidate(); + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + redraw(count + 1); + } + }, 100); + } + } + } + + public void onResume() { + redraw(0); //workaround for camera bug + } + public boolean isVisible() { return isVisible && placeProvider != null; } @@ -2774,7 +3254,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } private int getContainerViewHeight(int mode) { - int height = containerView.getHeight(); + //int height = containerView.getHeight(); + int height = AndroidUtilities.displaySize.y - AndroidUtilities.statusBarHeight; if (mode == 1) { height -= AndroidUtilities.dp(76); } else if (mode == 2) { @@ -2785,9 +3266,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private boolean onTouchEvent(MotionEvent ev) { if (animationInProgress != 0 || animationStartTime != 0) { - if (animationStartTime == 0) { - AndroidUtilities.unlockOrientation(parentActivity); - } return false; } @@ -2807,6 +3285,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } } + if (captionEditText.isEmojiPopupShowing() || captionEditText.isKeyboardVisible()) { + return true; + } + if (currentEditMode == 0 && ev.getPointerCount() == 1 && gestureDetector.onTouchEvent(ev)) { if (doubleTap) { doubleTap = false; @@ -2843,7 +3325,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat dragY = moveStartY = ev.getY(); draggingDown = false; canDragDown = true; - AndroidUtilities.lockOrientation(parentActivity); if (velocityTracker != null) { velocityTracker.clear(); } @@ -3000,8 +3481,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat moveToY = maxY; } animateTo(scale, moveToX, moveToY, false); - } else { - AndroidUtilities.unlockOrientation(parentActivity); } } return false; @@ -3048,7 +3527,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private void animateTo(float newScale, float newTx, float newTy, boolean isZoom, int duration) { if (scale == newScale && translationX == newTx && translationY == newTy) { - AndroidUtilities.unlockOrientation(parentActivity); return; } zoomAnimation = isZoom; @@ -3066,12 +3544,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat @Override public void onAnimationEnd(Object animation) { imageMoveAnimation = null; - AndroidUtilities.unlockOrientation(parentActivity); containerView.invalidate(); } }); imageMoveAnimation.start(); - AndroidUtilities.lockOrientation(parentActivity); } public void setAnimationValue(float value) { @@ -3122,7 +3598,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat photoCropView.setAnimationProgress(1); } updateMinMax(scale); - AndroidUtilities.unlockOrientation(parentActivity); zoomAnimation = false; } if (!scroller.isFinished()) { @@ -3298,20 +3773,14 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat updateMinMax(scale); if (checkImageView != null) { - checkImageView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + checkImageView.post(new Runnable() { @Override - public boolean onPreDraw() { - checkImageView.getViewTreeObserver().removeOnPreDrawListener(this); - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) checkImageView.getLayoutParams(); + public void run() { + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) checkImageView.getLayoutParams(); WindowManager manager = (WindowManager) ApplicationLoader.applicationContext.getSystemService(Activity.WINDOW_SERVICE); int rotation = manager.getDefaultDisplay().getRotation(); - if (rotation == Surface.ROTATION_270 || rotation == Surface.ROTATION_90) { - layoutParams.topMargin = AndroidUtilities.dp(58); - } else { - layoutParams.topMargin = AndroidUtilities.dp(68); - } + layoutParams.topMargin = AndroidUtilities.dp(rotation == Surface.ROTATION_270 || rotation == Surface.ROTATION_90 ? 58 : 68); checkImageView.setLayoutParams(layoutParams); - return false; } }); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PopupNotificationActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PopupNotificationActivity.java index b8a8832d8..d06f17a6d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PopupNotificationActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PopupNotificationActivity.java @@ -52,7 +52,9 @@ import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.ChatActivityEnterView; import org.telegram.ui.Components.FrameLayoutFixed; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.PopupAudioView; +import org.telegram.ui.Components.RecordStatusDrawable; import org.telegram.ui.Components.SizeNotifierRelativeLayout; import org.telegram.ui.Components.TypingDotsDrawable; @@ -78,6 +80,7 @@ public class PopupNotificationActivity extends Activity implements NotificationC private ArrayList audioViews = new ArrayList<>(); private VelocityTracker velocityTracker = null; private TypingDotsDrawable typingDotsDrawable; + private RecordStatusDrawable recordStatusDrawable; private int classGuid; private TLRPC.User currentUser; @@ -159,6 +162,7 @@ public class PopupNotificationActivity extends Activity implements NotificationC NotificationCenter.getInstance().addObserver(this, NotificationCenter.emojiDidLoaded); typingDotsDrawable = new TypingDotsDrawable(); + recordStatusDrawable = new RecordStatusDrawable(); SizeNotifierRelativeLayout contentView = new SizeNotifierRelativeLayout(this); setContentView(contentView); @@ -167,15 +171,15 @@ public class PopupNotificationActivity extends Activity implements NotificationC RelativeLayout relativeLayout = new RelativeLayout(this); contentView.addView(relativeLayout); RelativeLayout.LayoutParams layoutParams3 = (RelativeLayout.LayoutParams) relativeLayout.getLayoutParams(); - layoutParams3.width = RelativeLayout.LayoutParams.MATCH_PARENT; - layoutParams3.height = RelativeLayout.LayoutParams.MATCH_PARENT; + layoutParams3.width = LayoutHelper.MATCH_PARENT; + layoutParams3.height = LayoutHelper.MATCH_PARENT; relativeLayout.setLayoutParams(layoutParams3); RelativeLayout popupContainer = new RelativeLayout(this); popupContainer.setBackgroundColor(0xffffffff); relativeLayout.addView(popupContainer); layoutParams3 = (RelativeLayout.LayoutParams) popupContainer.getLayoutParams(); - layoutParams3.width = RelativeLayout.LayoutParams.MATCH_PARENT; + layoutParams3.width = LayoutHelper.MATCH_PARENT; layoutParams3.height = AndroidUtilities.dp(240); layoutParams3.leftMargin = AndroidUtilities.dp(12); layoutParams3.rightMargin = AndroidUtilities.dp(12); @@ -188,8 +192,8 @@ public class PopupNotificationActivity extends Activity implements NotificationC chatActivityEnterView = new ChatActivityEnterView(this, contentView, null, false); popupContainer.addView(chatActivityEnterView); layoutParams3 = (RelativeLayout.LayoutParams) chatActivityEnterView.getLayoutParams(); - layoutParams3.width = RelativeLayout.LayoutParams.MATCH_PARENT; - layoutParams3.height = RelativeLayout.LayoutParams.WRAP_CONTENT; + layoutParams3.width = LayoutHelper.MATCH_PARENT; + layoutParams3.height = LayoutHelper.WRAP_CONTENT; layoutParams3.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); chatActivityEnterView.setLayoutParams(layoutParams3); chatActivityEnterView.setDelegate(new ChatActivityEnterView.ChatActivityEnterViewDelegate() { @@ -214,7 +218,7 @@ public class PopupNotificationActivity extends Activity implements NotificationC @Override public void needSendTyping() { if (currentMessageObject != null) { - MessagesController.getInstance().sendTyping(currentMessageObject.getDialogId(), classGuid); + MessagesController.getInstance().sendTyping(currentMessageObject.getDialogId(), 0, classGuid); } } @@ -256,8 +260,8 @@ public class PopupNotificationActivity extends Activity implements NotificationC avatarContainer.setPadding(AndroidUtilities.dp(4), 0, AndroidUtilities.dp(4), 0); actionBar.addView(avatarContainer); FrameLayout.LayoutParams layoutParams2 = (FrameLayout.LayoutParams) avatarContainer.getLayoutParams(); - layoutParams2.height = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams2.height = LayoutHelper.MATCH_PARENT; + layoutParams2.width = LayoutHelper.WRAP_CONTENT; layoutParams2.rightMargin = AndroidUtilities.dp(48); layoutParams2.leftMargin = AndroidUtilities.dp(60); layoutParams2.gravity = Gravity.TOP | Gravity.LEFT; @@ -283,8 +287,8 @@ public class PopupNotificationActivity extends Activity implements NotificationC nameTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); avatarContainer.addView(nameTextView); layoutParams2 = (FrameLayout.LayoutParams) nameTextView.getLayoutParams(); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams2.width = LayoutHelper.WRAP_CONTENT; + layoutParams2.height = LayoutHelper.WRAP_CONTENT; layoutParams2.leftMargin = AndroidUtilities.dp(54); layoutParams2.bottomMargin = AndroidUtilities.dp(22); layoutParams2.gravity = Gravity.BOTTOM; @@ -300,8 +304,8 @@ public class PopupNotificationActivity extends Activity implements NotificationC onlineTextView.setGravity(Gravity.LEFT); avatarContainer.addView(onlineTextView); layoutParams2 = (FrameLayout.LayoutParams) onlineTextView.getLayoutParams(); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams2.width = LayoutHelper.WRAP_CONTENT; + layoutParams2.height = LayoutHelper.WRAP_CONTENT; layoutParams2.leftMargin = AndroidUtilities.dp(54); layoutParams2.bottomMargin = AndroidUtilities.dp(4); layoutParams2.gravity = Gravity.BOTTOM; @@ -489,7 +493,7 @@ public class PopupNotificationActivity extends Activity implements NotificationC } private void applyViewsLayoutParams(int xOffset) { - FrameLayout.LayoutParams layoutParams = null; + FrameLayout.LayoutParams layoutParams; int widht = AndroidUtilities.displaySize.x - AndroidUtilities.dp(24); if (leftView != null) { layoutParams = (FrameLayout.LayoutParams) leftView.getLayoutParams(); @@ -527,7 +531,7 @@ public class PopupNotificationActivity extends Activity implements NotificationC } else if (num == NotificationsController.getInstance().popupMessages.size()) { num = 0; } - ViewGroup view = null; + ViewGroup view; MessageObject messageObject = NotificationsController.getInstance().popupMessages.get(num); if (messageObject.type == 1 || messageObject.type == 4) { if (imageViews.size() > 0) { @@ -591,7 +595,7 @@ public class PopupNotificationActivity extends Activity implements NotificationC imageView.setImage(currentUrl, null, null); } } else if (messageObject.type == 2) { - PopupAudioView cell = null; + PopupAudioView cell; if (audioViews.size() > 0) { view = audioViews.get(0); audioViews.remove(0); @@ -955,9 +959,18 @@ public class PopupNotificationActivity extends Activity implements NotificationC } if (start) { try { - onlineTextView.setCompoundDrawablesWithIntrinsicBounds(typingDotsDrawable, null, null, null); - onlineTextView.setCompoundDrawablePadding(AndroidUtilities.dp(4)); - typingDotsDrawable.start(); + Integer type = MessagesController.getInstance().printingStringsTypes.get(currentMessageObject.getDialogId()); + if (type == 0) { + onlineTextView.setCompoundDrawablesWithIntrinsicBounds(typingDotsDrawable, null, null, null); + onlineTextView.setCompoundDrawablePadding(AndroidUtilities.dp(4)); + typingDotsDrawable.start(); + recordStatusDrawable.stop(); + } else if (type == 1) { + onlineTextView.setCompoundDrawablesWithIntrinsicBounds(recordStatusDrawable, null, null, null); + onlineTextView.setCompoundDrawablePadding(AndroidUtilities.dp(4)); + recordStatusDrawable.start(); + typingDotsDrawable.stop(); + } } catch (Exception e) { FileLog.e("tmessages", e); } @@ -965,6 +978,7 @@ public class PopupNotificationActivity extends Activity implements NotificationC onlineTextView.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null); onlineTextView.setCompoundDrawablePadding(0); typingDotsDrawable.stop(); + recordStatusDrawable.stop(); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PrivacySettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PrivacySettingsActivity.java index 6f5d0e901..6b6449365 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PrivacySettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PrivacySettingsActivity.java @@ -12,7 +12,6 @@ import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; -import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -37,6 +36,7 @@ import org.telegram.ui.Adapters.BaseFragmentAdapter; import org.telegram.ui.Cells.HeaderCell; import org.telegram.ui.Cells.TextInfoPrivacyCell; import org.telegram.ui.Cells.TextSettingsCell; +import org.telegram.ui.Components.LayoutHelper; import java.util.ArrayList; @@ -114,12 +114,7 @@ public class PrivacySettingsActivity extends BaseFragment implements Notificatio listView.setDividerHeight(0); listView.setVerticalScrollBarEnabled(false); listView.setDrawSelectorOnTop(true); - frameLayout.addView(listView); - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.gravity = Gravity.TOP; - listView.setLayoutParams(layoutParams); + frameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); listView.setAdapter(listAdapter); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override @@ -183,7 +178,7 @@ public class PrivacySettingsActivity extends BaseFragment implements Notificatio } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } else if (i == lastSeenRow) { presentFragment(new LastSeenActivity()); } else if (i == passwordRow) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java index 99fae5d7e..980c185be 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java @@ -10,6 +10,7 @@ package org.telegram.ui; import android.animation.ObjectAnimator; import android.animation.StateListAnimator; +import android.annotation.SuppressLint; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; @@ -54,7 +55,8 @@ import org.telegram.android.MessageObject; import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; import org.telegram.ui.Adapters.BaseFragmentAdapter; -import org.telegram.ui.AnimationCompat.ViewProxy; +import org.telegram.android.AnimationCompat.ViewProxy; +import org.telegram.ui.Cells.AddMemberCell; import org.telegram.ui.Cells.DividerCell; import org.telegram.ui.Cells.EmptyCell; import org.telegram.ui.Cells.ShadowSectionCell; @@ -69,6 +71,7 @@ import org.telegram.ui.Components.AvatarUpdater; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.Components.IdenticonDrawable; +import org.telegram.ui.Components.LayoutHelper; import java.util.ArrayList; import java.util.Collections; @@ -125,6 +128,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. private int sectionRow; private int membersSectionRow; private int membersEndRow; + private int addMemberRow; private int rowCount = 0; public ProfileActivity(Bundle args) { @@ -258,7 +262,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } else if (id == add_contact) { TLRPC.User user = MessagesController.getInstance().getUser(user_id); Bundle args = new Bundle(); @@ -292,31 +296,9 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } else if (id == add_member) { - Bundle args = new Bundle(); - args.putBoolean("onlyUsers", true); - args.putBoolean("destroyAfterSelect", true); - args.putBoolean("returnAsResult", true); - //args.putBoolean("allowUsernameSearch", false); - if (chat_id > 0) { - args.putString("selectAlertString", LocaleController.getString("AddToTheGroup", R.string.AddToTheGroup)); - } - ContactsActivity fragment = new ContactsActivity(args); - fragment.setDelegate(new ContactsActivity.ContactsActivityDelegate() { - @Override - public void didSelectContact(TLRPC.User user, String param) { - MessagesController.getInstance().addUserToChat(chat_id, user, info, param != null ? Utilities.parseInt(param) : 0); - } - }); - if (info != null) { - HashMap users = new HashMap<>(); - for (TLRPC.TL_chatParticipant p : info.participants) { - users.put(p.user_id, null); - } - fragment.setIgnoreUsers(users); - } - presentFragment(fragment); + openAddMember(); } else if (id == leave_group) { AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); builder.setMessage(LocaleController.getString("AreYouSureDeleteAndExit", R.string.AreYouSureDeleteAndExit)); @@ -328,7 +310,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } else if (id == edit_name) { Bundle args = new Bundle(); args.putInt("chat_id", chat_id); @@ -385,8 +367,8 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. nameTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); actionBar.addView(nameTextView); layoutParams = (FrameLayout.LayoutParams) nameTextView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : 97); layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 97 : 16); layoutParams.bottomMargin = AndroidUtilities.dp(51); @@ -403,8 +385,8 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. onlineTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT)); actionBar.addView(onlineTextView); layoutParams = (FrameLayout.LayoutParams) onlineTextView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : 97); layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 97 : 16); layoutParams.bottomMargin = AndroidUtilities.dp(30); @@ -418,8 +400,8 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. AndroidUtilities.setListViewEdgeEffectColor(listView, AvatarDrawable.getProfileBackColorForId(user_id != 0 ? 5 : chat_id)); frameLayout.addView(listView); layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP; listView.setLayoutParams(layoutParams); @@ -446,7 +428,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. if (getParentActivity() == null) { return; } - showAlertDialog(AndroidUtilities.buildTTLAlert(getParentActivity(), currentEncryptedChat)); + showDialog(AndroidUtilities.buildTTLAlert(getParentActivity(), currentEncryptedChat).create()); } else if (i == settingsNotificationsRow) { Bundle args = new Bundle(); if (user_id != 0) { @@ -467,7 +449,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } else if (i == phoneRow) { final TLRPC.User user = MessagesController.getInstance().getUser(user_id); if (user == null || user.phone == null || user.phone.length() == 0 || getParentActivity() == null) { @@ -498,7 +480,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. } } }); - showAlertDialog(builder); + showDialog(builder.create()); } else if (i > emptyRowChat2 && i < membersEndRow) { int user_id = info.participants.get(sortedUsers.get(i - emptyRowChat2 - 1)).user_id; if (user_id == UserConfig.getClientUserId()) { @@ -507,6 +489,8 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. Bundle args = new Bundle(); args.putInt("user_id", user_id); presentFragment(new ProfileActivity(args)); + } else if (i == addMemberRow) { + openAddMember(); } } }); @@ -539,7 +523,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. } } }); - showAlertDialog(builder); + showDialog(builder.create()); return true; } @@ -572,6 +556,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. animator.addState(new int[]{}, ObjectAnimator.ofFloat(writeButton, "translationZ", AndroidUtilities.dp(4), AndroidUtilities.dp(2)).setDuration(200)); writeButton.setStateListAnimator(animator); writeButton.setOutlineProvider(new ViewOutlineProvider() { + @SuppressLint("NewApi") @Override public void getOutline(View view, Outline outline) { outline.setOval(0, 0, AndroidUtilities.dp(56), AndroidUtilities.dp(56)); @@ -579,8 +564,8 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. }); } layoutParams = (FrameLayout.LayoutParams) writeButton.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : 0); layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 0 : 16); layoutParams.gravity = (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT); @@ -624,7 +609,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. } } }); - showAlertDialog(builder); + showDialog(builder.create()); } } }); @@ -671,6 +656,35 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. } } + private void openAddMember() { + Bundle args = new Bundle(); + args.putBoolean("onlyUsers", true); + args.putBoolean("destroyAfterSelect", true); + args.putBoolean("returnAsResult", true); + if (info != null && info.admin_id == UserConfig.getClientUserId()) { + args.putInt("chat_id", currentChat.id); + } + //args.putBoolean("allowUsernameSearch", false); + if (chat_id > 0) { + args.putString("selectAlertString", LocaleController.getString("AddToTheGroup", R.string.AddToTheGroup)); + } + ContactsActivity fragment = new ContactsActivity(args); + fragment.setDelegate(new ContactsActivity.ContactsActivityDelegate() { + @Override + public void didSelectContact(TLRPC.User user, String param) { + MessagesController.getInstance().addUserToChat(chat_id, user, info, param != null ? Utilities.parseInt(param) : 0); + } + }); + if (info != null) { + HashMap users = new HashMap<>(); + for (TLRPC.TL_chatParticipant p : info.participants) { + users.put(p.user_id, null); + } + fragment.setIgnoreUsers(users); + } + presentFragment(fragment); + } + private void checkListViewScroll() { if (listView.getChildCount() == 0) { return; @@ -1080,16 +1094,18 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. rowCount += info.participants.size(); membersEndRow = rowCount; int maxCount = chat_id > 0 ? MessagesController.getInstance().maxGroupCount : MessagesController.getInstance().maxBroadcastCount; + addMemberRow = rowCount++; } else { membersEndRow = -1; membersSectionRow = -1; emptyRowChat2 = -1; + addMemberRow = -1; } } } private void updateProfileData() { - if (avatarImage == null) { + if (avatarImage == null || nameTextView == null) { return; } if (user_id != 0) { @@ -1115,6 +1131,8 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. TLRPC.Chat chat = MessagesController.getInstance().getChat(chat_id); if (chat != null) { currentChat = chat; + } else { + chat = currentChat; } nameTextView.setText(chat.title); @@ -1227,7 +1245,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. if (user_id != 0) { return i == phoneRow || i == settingsTimerRow || i == settingsKeyRow || i == settingsNotificationsRow || i == sharedMediaRow || i == startSecretChatRow; } else if (chat_id != 0) { - return i == settingsNotificationsRow || i == sharedMediaRow || i > emptyRowChat2 && i < membersEndRow; + return i == settingsNotificationsRow || i == sharedMediaRow || i > emptyRowChat2 && i < membersEndRow || i == addMemberRow; } return false; } @@ -1260,11 +1278,11 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. view = new EmptyCell(mContext); } if (i == overscrollRow) { - ((EmptyCell) view).setHeight(88); + ((EmptyCell) view).setHeight(AndroidUtilities.dp(88)); } else if (i == emptyRowChat || i == emptyRowChat2) { - ((EmptyCell) view).setHeight(8); + ((EmptyCell) view).setHeight(AndroidUtilities.dp(8)); } else { - ((EmptyCell) view).setHeight(36); + ((EmptyCell) view).setHeight(AndroidUtilities.dp(36)); } } else if (type == 1) { if (view == null) { @@ -1341,6 +1359,10 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. if (view == null) { view = new ShadowSectionCell(mContext); } + } else if (type == 6) { + if (view == null) { + view = new AddMemberCell(mContext); + } } return view; } @@ -1359,13 +1381,15 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. return 4; } else if (i == membersSectionRow) { return 5; + } else if (i == addMemberRow) { + return 6; } return 0; } @Override public int getViewTypeCount() { - return 6; + return 7; } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ProfileNotificationsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ProfileNotificationsActivity.java index e845fd4de..103ac0c18 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ProfileNotificationsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ProfileNotificationsActivity.java @@ -20,12 +20,16 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.provider.Settings; +import android.util.TypedValue; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.FrameLayout; +import android.widget.LinearLayout; import android.widget.ListView; +import android.widget.TextView; import org.telegram.android.AndroidUtilities; import org.telegram.android.MessagesController; @@ -45,6 +49,8 @@ import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.ColorPickerView; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.NumberPicker; public class ProfileNotificationsActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { @@ -56,6 +62,7 @@ public class ProfileNotificationsActivity extends BaseFragment implements Notifi private int settingsSoundRow; private int settingsPriorityRow; private int settingsLedRow; + private int smartRow; private int rowCount = 0; public ProfileNotificationsActivity(Bundle args) { @@ -73,6 +80,12 @@ public class ProfileNotificationsActivity extends BaseFragment implements Notifi } else { settingsPriorityRow = -1; } + int lower_id = (int) dialog_id; + if (lower_id < 0) { + smartRow = rowCount++; + } else { + smartRow = 1; + } settingsLedRow = rowCount++; NotificationCenter.getInstance().addObserver(this, NotificationCenter.notificationsSettingsUpdated); return super.onFragmentCreate(); @@ -107,9 +120,9 @@ public class ProfileNotificationsActivity extends BaseFragment implements Notifi listView.setVerticalScrollBarEnabled(false); AndroidUtilities.setListViewEdgeEffectColor(listView, AvatarDrawable.getProfileBackColorForId(5)); frameLayout.addView(listView); - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + final FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; listView.setLayoutParams(layoutParams); listView.setAdapter(new ListAdapter(context)); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @@ -147,7 +160,7 @@ public class ProfileNotificationsActivity extends BaseFragment implements Notifi } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } else if (i == settingsNotificationsRow) { if (getParentActivity() == null) { return; @@ -180,7 +193,7 @@ public class ProfileNotificationsActivity extends BaseFragment implements Notifi } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } else if (i == settingsSoundRow) { try { Intent tmpIntent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER); @@ -215,9 +228,10 @@ public class ProfileNotificationsActivity extends BaseFragment implements Notifi return; } - LayoutInflater li = (LayoutInflater) getParentActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); - view = li.inflate(R.layout.settings_color_dialog_layout, null, false); - final ColorPickerView colorPickerView = (ColorPickerView) view.findViewById(R.id.color_picker); + LinearLayout linearLayout = new LinearLayout(getParentActivity()); + linearLayout.setOrientation(LinearLayout.VERTICAL); + final ColorPickerView colorPickerView = new ColorPickerView(getParentActivity()); + linearLayout.addView(colorPickerView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); if (preferences.contains("color_" + dialog_id)) { @@ -232,7 +246,7 @@ public class ProfileNotificationsActivity extends BaseFragment implements Notifi AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); builder.setTitle(LocaleController.getString("LedColor", R.string.LedColor)); - builder.setView(view); + builder.setView(linearLayout); builder.setPositiveButton(LocaleController.getString("Set", R.string.Set), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int which) { @@ -263,7 +277,7 @@ public class ProfileNotificationsActivity extends BaseFragment implements Notifi listView.invalidateViews(); } }); - showAlertDialog(builder); + showDialog(builder.create()); } else if (i == settingsPriorityRow) { AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); builder.setTitle(LocaleController.getString("NotificationsPriority", R.string.NotificationsPriority)); @@ -288,7 +302,122 @@ public class ProfileNotificationsActivity extends BaseFragment implements Notifi } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); + } else if (i == smartRow) { + if (getParentActivity() == null) { + return; + } + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); + int notifyMaxCount = preferences.getInt("smart_max_count_" + dialog_id, 2); + int notifyDelay = preferences.getInt("smart_delay_" + dialog_id, 3 * 60); + if (notifyMaxCount == 0) { + notifyMaxCount = 2; + } + + LinearLayout linearLayout = new LinearLayout(getParentActivity()); + linearLayout.setOrientation(LinearLayout.VERTICAL); + + LinearLayout linearLayout2 = new LinearLayout(getParentActivity()); + linearLayout2.setOrientation(LinearLayout.HORIZONTAL); + linearLayout.addView(linearLayout2); + LinearLayout.LayoutParams layoutParams1 = (LinearLayout.LayoutParams) linearLayout2.getLayoutParams(); + layoutParams1.width = LayoutHelper.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; + layoutParams1.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP; + linearLayout2.setLayoutParams(layoutParams1); + + TextView textView = new TextView(getParentActivity()); + textView.setText(LocaleController.getString("SmartNotificationsSoundAtMost", R.string.SmartNotificationsSoundAtMost)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); + linearLayout2.addView(textView); + layoutParams1 = (LinearLayout.LayoutParams) textView.getLayoutParams(); + layoutParams1.width = LayoutHelper.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; + layoutParams1.gravity = Gravity.CENTER_VERTICAL | Gravity.LEFT; + textView.setLayoutParams(layoutParams1); + + final NumberPicker numberPickerTimes = new NumberPicker(getParentActivity()); + numberPickerTimes.setMinValue(1); + numberPickerTimes.setMaxValue(10); + numberPickerTimes.setValue(notifyMaxCount); + linearLayout2.addView(numberPickerTimes); + layoutParams1 = (LinearLayout.LayoutParams) numberPickerTimes.getLayoutParams(); + layoutParams1.width = AndroidUtilities.dp(50); + numberPickerTimes.setLayoutParams(layoutParams1); + + textView = new TextView(getParentActivity()); + textView.setText(LocaleController.getString("SmartNotificationsTimes", R.string.SmartNotificationsTimes)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); + linearLayout2.addView(textView); + layoutParams1 = (LinearLayout.LayoutParams) textView.getLayoutParams(); + layoutParams1.width = LayoutHelper.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; + layoutParams1.gravity = Gravity.CENTER_VERTICAL | Gravity.LEFT; + textView.setLayoutParams(layoutParams1); + + linearLayout2 = new LinearLayout(getParentActivity()); + linearLayout2.setOrientation(LinearLayout.HORIZONTAL); + linearLayout.addView(linearLayout2); + layoutParams1 = (LinearLayout.LayoutParams) linearLayout2.getLayoutParams(); + layoutParams1.width = LayoutHelper.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; + layoutParams1.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP; + linearLayout2.setLayoutParams(layoutParams1); + + textView = new TextView(getParentActivity()); + textView.setText(LocaleController.getString("SmartNotificationsWithin", R.string.SmartNotificationsWithin)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); + linearLayout2.addView(textView); + layoutParams1 = (LinearLayout.LayoutParams) textView.getLayoutParams(); + layoutParams1.width = LayoutHelper.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; + layoutParams1.gravity = Gravity.CENTER_VERTICAL | Gravity.LEFT; + textView.setLayoutParams(layoutParams1); + + final NumberPicker numberPickerMinutes = new NumberPicker(getParentActivity()); + numberPickerMinutes.setMinValue(1); + numberPickerMinutes.setMaxValue(10); + numberPickerMinutes.setValue(notifyDelay / 60); + linearLayout2.addView(numberPickerMinutes); + layoutParams1 = (LinearLayout.LayoutParams) numberPickerMinutes.getLayoutParams(); + layoutParams1.width = AndroidUtilities.dp(50); + numberPickerMinutes.setLayoutParams(layoutParams1); + + textView = new TextView(getParentActivity()); + textView.setText(LocaleController.getString("SmartNotificationsMinutes", R.string.SmartNotificationsMinutes)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); + linearLayout2.addView(textView); + layoutParams1 = (LinearLayout.LayoutParams) textView.getLayoutParams(); + layoutParams1.width = LayoutHelper.WRAP_CONTENT; + layoutParams1.height = LayoutHelper.WRAP_CONTENT; + layoutParams1.gravity = Gravity.CENTER_VERTICAL | Gravity.LEFT; + textView.setLayoutParams(layoutParams1); + + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + builder.setTitle(LocaleController.getString("SmartNotifications", R.string.SmartNotifications)); + builder.setView(linearLayout); + builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); + preferences.edit().putInt("smart_max_count_" + dialog_id, numberPickerTimes.getValue()).commit(); + preferences.edit().putInt("smart_delay_" + dialog_id, numberPickerMinutes.getValue() * 60).commit(); + if (listView != null) { + listView.invalidateViews(); + } + } + }); + builder.setNegativeButton(LocaleController.getString("SmartNotificationsDisabled", R.string.SmartNotificationsDisabled), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); + preferences.edit().putInt("smart_max_count_" + dialog_id, 0).commit(); + if (listView != null) { + listView.invalidateViews(); + } + } + }); + showDialog(builder.create()); } } }); @@ -447,6 +576,16 @@ public class ProfileNotificationsActivity extends BaseFragment implements Notifi } else if (value == 3) { textCell.setTextAndValue(LocaleController.getString("NotificationsPriority", R.string.NotificationsPriority), LocaleController.getString("SettingsDefault", R.string.SettingsDefault), true); } + } else if (i == smartRow) { + int notifyMaxCount = preferences.getInt("smart_max_count_" + dialog_id, 2); + int notifyDelay = preferences.getInt("smart_delay_" + dialog_id, 3 * 60); + if (notifyMaxCount == 0) { + textCell.setTextAndValue(LocaleController.getString("SmartNotifications", R.string.SmartNotifications), LocaleController.getString("SmartNotificationsDisabled", R.string.SmartNotificationsDisabled), true); + } else { + String times = LocaleController.formatPluralString("Times", notifyMaxCount); + String minutes = LocaleController.formatPluralString("Minutes", notifyDelay / 60); + textCell.setTextAndValue(LocaleController.getString("SmartNotifications", R.string.SmartNotifications), LocaleController.formatString("SmartNotificationsInfo", R.string.SmartNotificationsInfo, times, minutes), true); + } } } else if (type == 1) { if (view == null) { @@ -472,7 +611,7 @@ public class ProfileNotificationsActivity extends BaseFragment implements Notifi @Override public int getItemViewType(int i) { - if (i == settingsNotificationsRow || i == settingsVibrateRow || i == settingsSoundRow || i == settingsPriorityRow) { + if (i == settingsNotificationsRow || i == settingsVibrateRow || i == settingsSoundRow || i == settingsPriorityRow || i == smartRow) { return 0; } else if (i == settingsLedRow) { return 1; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SecretPhotoViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/SecretPhotoViewer.java index ee5928a7e..368898919 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SecretPhotoViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SecretPhotoViewer.java @@ -39,6 +39,7 @@ import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; import org.telegram.messenger.R; import org.telegram.messenger.TLRPC; +import org.telegram.ui.Components.LayoutHelper; import java.io.File; import java.util.ArrayList; @@ -213,8 +214,8 @@ public class SecretPhotoViewer implements NotificationCenter.NotificationCenterD containerView.setFocusable(false); windowView.addView(containerView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)containerView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; containerView.setLayoutParams(layoutParams); containerView.setOnTouchListener(new View.OnTouchListener() { @@ -264,7 +265,7 @@ public class SecretPhotoViewer implements NotificationCenter.NotificationCenterD BitmapDrawable drawable = ImageLoader.getInstance().getImageFromMemory(sizeFull.location, null, null); if (drawable == null) { File file = FileLoader.getPathToAttach(sizeFull); - Bitmap bitmap = null; + Bitmap bitmap; try { bitmap = BitmapFactory.decodeFile(file.getAbsolutePath()); } catch (Throwable e) { @@ -279,7 +280,7 @@ public class SecretPhotoViewer implements NotificationCenter.NotificationCenterD if (drawable != null) { centerImage.setImageBitmap(drawable); } else { - centerImage.setImage(sizeFull.location, null, null, size, false); + centerImage.setImage(sizeFull.location, null, null, size, null, false); } currentMessageObject = messageObject; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SessionsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/SessionsActivity.java index fb90c3100..a7185bc3d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SessionsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SessionsActivity.java @@ -47,6 +47,7 @@ import org.telegram.ui.Cells.HeaderCell; import org.telegram.ui.Cells.SessionCell; import org.telegram.ui.Cells.TextInfoPrivacyCell; import org.telegram.ui.Cells.TextSettingsCell; +import org.telegram.ui.Components.LayoutHelper; import java.util.ArrayList; @@ -114,8 +115,8 @@ public class SessionsActivity extends BaseFragment implements NotificationCenter imageView.setImageResource(R.drawable.devices); emptyLayout.addView(imageView); LinearLayout.LayoutParams layoutParams2 = (LinearLayout.LayoutParams) imageView.getLayoutParams(); - layoutParams2.width = LinearLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = LinearLayout.LayoutParams.WRAP_CONTENT; + layoutParams2.width = LayoutHelper.WRAP_CONTENT; + layoutParams2.height = LayoutHelper.WRAP_CONTENT; imageView.setLayoutParams(layoutParams2); TextView textView = new TextView(context); @@ -127,8 +128,8 @@ public class SessionsActivity extends BaseFragment implements NotificationCenter emptyLayout.addView(textView); layoutParams2 = (LinearLayout.LayoutParams) textView.getLayoutParams(); layoutParams2.topMargin = AndroidUtilities.dp(16); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams2.width = LayoutHelper.WRAP_CONTENT; + layoutParams2.height = LayoutHelper.WRAP_CONTENT; layoutParams2.gravity = Gravity.CENTER; textView.setLayoutParams(layoutParams2); @@ -141,16 +142,16 @@ public class SessionsActivity extends BaseFragment implements NotificationCenter emptyLayout.addView(textView); layoutParams2 = (LinearLayout.LayoutParams) textView.getLayoutParams(); layoutParams2.topMargin = AndroidUtilities.dp(14); - layoutParams2.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams2.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams2.width = LayoutHelper.WRAP_CONTENT; + layoutParams2.height = LayoutHelper.WRAP_CONTENT; layoutParams2.gravity = Gravity.CENTER; textView.setLayoutParams(layoutParams2); FrameLayout progressView = new FrameLayout(context); frameLayout.addView(progressView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) progressView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; progressView.setLayoutParams(layoutParams); progressView.setOnTouchListener(new View.OnTouchListener() { @Override @@ -162,8 +163,8 @@ public class SessionsActivity extends BaseFragment implements NotificationCenter ProgressBar progressBar = new ProgressBar(context); progressView.addView(progressBar); layoutParams = (FrameLayout.LayoutParams) progressView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER; progressView.setLayoutParams(layoutParams); @@ -175,8 +176,8 @@ public class SessionsActivity extends BaseFragment implements NotificationCenter listView.setEmptyView(progressView); frameLayout.addView(listView); layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP; listView.setLayoutParams(layoutParams); listView.setAdapter(listAdapter); @@ -223,7 +224,7 @@ public class SessionsActivity extends BaseFragment implements NotificationCenter } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } else if (i >= otherSessionsStartRow && i < otherSessionsEndRow) { AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); builder.setMessage(LocaleController.getString("TerminateSessionQuestion", R.string.TerminateSessionQuestion)); @@ -265,7 +266,7 @@ public class SessionsActivity extends BaseFragment implements NotificationCenter } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } } }); @@ -335,7 +336,11 @@ public class SessionsActivity extends BaseFragment implements NotificationCenter currentSessionSectionRow = -1; } if (sessions.isEmpty()) { - noOtherSessionsRow = -1; + if (currentSession != null) { + noOtherSessionsRow = rowCount++; + } else { + noOtherSessionsRow = -1; + } terminateAllSessionsRow = -1; terminateAllSessionsDetailRow = -1; otherSessionsSectionRow = -1; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java index f6ac527a5..a5bdf3b00 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java @@ -10,6 +10,7 @@ package org.telegram.ui; import android.animation.ObjectAnimator; import android.animation.StateListAnimator; +import android.annotation.SuppressLint; import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; @@ -53,7 +54,6 @@ import org.telegram.messenger.BuildVars; import org.telegram.android.LocaleController; import org.telegram.messenger.FileLoader; import org.telegram.messenger.SerializedData; -import org.telegram.messenger.TLClassStore; import org.telegram.messenger.TLObject; import org.telegram.messenger.TLRPC; import org.telegram.messenger.ConnectionsManager; @@ -66,7 +66,7 @@ import org.telegram.messenger.RPCRequest; import org.telegram.messenger.UserConfig; import org.telegram.android.MessageObject; import org.telegram.ui.Adapters.BaseFragmentAdapter; -import org.telegram.ui.AnimationCompat.ViewProxy; +import org.telegram.android.AnimationCompat.ViewProxy; import org.telegram.ui.Cells.TextInfoCell; import org.telegram.ui.Cells.EmptyCell; import org.telegram.ui.Cells.HeaderCell; @@ -81,6 +81,7 @@ import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.AvatarUpdater; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.NumberPicker; import java.io.File; @@ -118,6 +119,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter private int messagesSectionRow; private int messagesSectionRow2; private int textSizeRow; + private int stickersRow; private int sendByEnterRow; private int supportSectionRow; private int supportSectionRow2; @@ -176,7 +178,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter if (user == null) { return; } - TLRPC.TL_photos_photo photo = (TLRPC.TL_photos_photo)response; + TLRPC.TL_photos_photo photo = (TLRPC.TL_photos_photo) response; ArrayList sizes = photo.photo.sizes; TLRPC.PhotoSize smallSize = FileLoader.getClosestPhotoSizeWithSize(sizes, 100); TLRPC.PhotoSize bigSize = FileLoader.getClosestPhotoSizeWithSize(sizes, 1000); @@ -231,6 +233,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter messagesSectionRow = rowCount++; messagesSectionRow2 = rowCount++; textSizeRow = rowCount++; + stickersRow = rowCount++; sendByEnterRow = rowCount++; supportSectionRow = rowCount++; supportSectionRow2 = rowCount++; @@ -293,20 +296,11 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { - SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); - SharedPreferences.Editor editor = preferences.edit(); - editor.clear().commit(); - MessagesController.getInstance().unregistedPush(); - MessagesController.getInstance().logOut(); - UserConfig.clearConfig(); - NotificationCenter.getInstance().postNotificationName(NotificationCenter.appDidLogout); - MessagesStorage.getInstance().cleanUp(false); - MessagesController.getInstance().cleanUp(); - ContactsController.getInstance().deleteAllAppAccounts(); + MessagesController.getInstance().performLogout(true); } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } } }); @@ -322,15 +316,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter avatarImage = new BackupImageView(context); avatarImage.setRoundRadius(AndroidUtilities.dp(30)); - actionBar.addView(avatarImage); - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) avatarImage.getLayoutParams(); - layoutParams.gravity = (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.BOTTOM; - layoutParams.width = AndroidUtilities.dp(60); - layoutParams.height = AndroidUtilities.dp(60); - layoutParams.leftMargin = LocaleController.isRTL ? 0 : AndroidUtilities.dp(17); - layoutParams.rightMargin = LocaleController.isRTL ? AndroidUtilities.dp(17) : 0; - layoutParams.bottomMargin = AndroidUtilities.dp(22); - avatarImage.setLayoutParams(layoutParams); + actionBar.addView(avatarImage, LayoutHelper.createFrame(60, 60, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.BOTTOM, LocaleController.isRTL ? 0 : 17, 0, LocaleController.isRTL ? 17 : 0, 22)); avatarImage.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -351,15 +337,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter nameTextView.setEllipsize(TextUtils.TruncateAt.END); nameTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT)); nameTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - actionBar.addView(nameTextView); - layoutParams = (FrameLayout.LayoutParams) nameTextView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : 97); - layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 97 : 16); - layoutParams.bottomMargin = AndroidUtilities.dp(51); - layoutParams.gravity = (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.BOTTOM; - nameTextView.setLayoutParams(layoutParams); + actionBar.addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.BOTTOM, LocaleController.isRTL ? 16 : 97, 0, LocaleController.isRTL ? 97 : 16, 51)); onlineTextView = new TextView(context); onlineTextView.setTextColor(AvatarDrawable.getProfileTextColorForId(5)); @@ -369,27 +347,14 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter onlineTextView.setSingleLine(true); onlineTextView.setEllipsize(TextUtils.TruncateAt.END); onlineTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT)); - actionBar.addView(onlineTextView); - layoutParams = (FrameLayout.LayoutParams) onlineTextView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : 97); - layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 97 : 16); - layoutParams.bottomMargin = AndroidUtilities.dp(30); - layoutParams.gravity = (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.BOTTOM; - onlineTextView.setLayoutParams(layoutParams); + actionBar.addView(onlineTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.BOTTOM, LocaleController.isRTL ? 16 : 97, 0, LocaleController.isRTL ? 97 : 16, 30)); listView = new ListView(context); listView.setDivider(null); listView.setDividerHeight(0); listView.setVerticalScrollBarEnabled(false); AndroidUtilities.setListViewEdgeEffectColor(listView, AvatarDrawable.getProfileBackColorForId(5)); - frameLayout.addView(listView); - layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.gravity = Gravity.TOP; - listView.setLayoutParams(layoutParams); + frameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT)); listView.setAdapter(listAdapter); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override @@ -418,7 +383,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter } } }); - showAlertDialog(builder); + showDialog(builder.create()); } else if (i == enableAnimationsRow) { SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); boolean animations = preferences.getBoolean("view_animations", true); @@ -451,7 +416,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } else if (i == sendLogsRow) { sendLogs(); } else if (i == clearLogsRow) { @@ -488,7 +453,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } else if (i == telegramFaqRow) { try { Intent pickIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(LocaleController.getString("TelegramFaqUrl", R.string.TelegramFaqUrl))); @@ -521,7 +486,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } else if (i == wifiDownloadRow || i == mobileDownloadRow || i == roamingDownloadRow) { if (getParentActivity() == null) { return; @@ -575,7 +540,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter if (i == mobileDownloadRow) { editor.putInt("mobileDataDownloadMask", mask); - mask = MediaController.getInstance().mobileDataDownloadMask = mask; + MediaController.getInstance().mobileDataDownloadMask = mask; } else if (i == wifiDownloadRow) { editor.putInt("wifiDownloadMask", mask); MediaController.getInstance().wifiDownloadMask = mask; @@ -590,11 +555,13 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter } }); builder.setNegativeButton(LocaleController.getString("OK", R.string.OK), null); - showAlertDialog(builder); + showDialog(builder.create()); } else if (i == usernameRow) { presentFragment(new ChangeUsernameActivity()); } else if (i == numberRow) { presentFragment(new ChangePhoneHelpActivity()); + } else if (i == stickersRow) { + presentFragment(new StickersActivity()); } } }); @@ -611,20 +578,14 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter animator.addState(new int[]{}, ObjectAnimator.ofFloat(writeButton, "translationZ", AndroidUtilities.dp(4), AndroidUtilities.dp(2)).setDuration(200)); writeButton.setStateListAnimator(animator); writeButton.setOutlineProvider(new ViewOutlineProvider() { + @SuppressLint("NewApi") @Override public void getOutline(View view, Outline outline) { outline.setOval(0, 0, AndroidUtilities.dp(56), AndroidUtilities.dp(56)); } }); } - frameLayout.addView(writeButton); - layoutParams = (FrameLayout.LayoutParams) writeButton.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.leftMargin = AndroidUtilities.dp(LocaleController.isRTL ? 16 : 0); - layoutParams.rightMargin = AndroidUtilities.dp(LocaleController.isRTL ? 0 : 16); - layoutParams.gravity = (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT); - writeButton.setLayoutParams(layoutParams); + frameLayout.addView(writeButton, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.TOP, LocaleController.isRTL ? 16 : 0, 0, LocaleController.isRTL ? 0 : 16, 0)); writeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -663,7 +624,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter } } }); - showAlertDialog(builder); + showDialog(builder.create()); } }); @@ -737,7 +698,8 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter } @Override - public void willSwitchFromPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index) { } + public void willSwitchFromPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index) { + } @Override public void willHidePhotoViewer() { @@ -745,19 +707,26 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter } @Override - public boolean isPhotoChecked(int index) { return false; } + public boolean isPhotoChecked(int index) { + return false; + } @Override - public void setPhotoChecked(int index) { } + public void setPhotoChecked(int index) { + } @Override - public void cancelButtonPressed() { } + public void cancelButtonPressed() { + } @Override - public void sendButtonPressed(int index) { } + public void sendButtonPressed(int index) { + } @Override - public int getSelectedCount() { return 0; } + public int getSelectedCount() { + return 0; + } public void performAskAQuestion() { final SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); @@ -772,7 +741,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter byte[] datacentersBytes = Base64.decode(userString, Base64.DEFAULT); if (datacentersBytes != null) { SerializedData data = new SerializedData(datacentersBytes); - supportUser = (TLRPC.User)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + supportUser = TLRPC.User.TLdeserialize(data, data.readInt32(false), false); if (supportUser != null && supportUser.id == 333000) { supportUser = null; } @@ -797,7 +766,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter public void run(TLObject response, TLRPC.TL_error error) { if (error == null) { - final TLRPC.TL_help_support res = (TLRPC.TL_help_support)response; + final TLRPC.TL_help_support res = (TLRPC.TL_help_support) response; AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { @@ -866,7 +835,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter @Override public void didReceivedNotification(int id, Object... args) { if (id == NotificationCenter.updateInterfaces) { - int mask = (Integer)args[0]; + int mask = (Integer) args[0]; if ((mask & MessagesController.UPDATE_MASK_AVATAR) != 0 || (mask & MessagesController.UPDATE_MASK_NAME) != 0) { updateUserData(); } @@ -898,14 +867,14 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter } if (avatarImage != null) { - float diff = actionBar.getExtraHeight() / (float)AndroidUtilities.dp(88); + float diff = actionBar.getExtraHeight() / (float) AndroidUtilities.dp(88); float diffm = 1.0f - diff; - int avatarSize = 42 + (int)(18 * diff); - int avatarX = 17 + (int)(47 * diffm); - int avatarY = AndroidUtilities.dp(22) - (int)((AndroidUtilities.dp(22) - (AndroidUtilities.getCurrentActionBarHeight() - AndroidUtilities.dp(42)) / 2) * (1.0f - diff)); - int nameX = 97 + (int)(21 * diffm); - int nameEndX = 16 + (int)(32 * diffm); + int avatarSize = 42 + (int) (18 * diff); + int avatarX = 17 + (int) (47 * diffm); + int avatarY = AndroidUtilities.dp(22) - (int) ((AndroidUtilities.dp(22) - (AndroidUtilities.getCurrentActionBarHeight() - AndroidUtilities.dp(42)) / 2) * (1.0f - diff)); + int nameX = 97 + (int) (21 * diffm); + int nameEndX = 16 + (int) (32 * diffm); int nameY = avatarY + AndroidUtilities.dp(29 - 13 * diffm); int statusY = avatarY + AndroidUtilities.dp(8 - 7 * diffm); float scale = 1.0f - 0.12f * diffm; @@ -987,7 +956,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter try { ArrayList uris = new ArrayList<>(); File sdCard = ApplicationLoader.applicationContext.getExternalFilesDir(null); - File dir = new File (sdCard.getAbsolutePath() + "/logs"); + File dir = new File(sdCard.getAbsolutePath() + "/logs"); File[] files = dir.listFiles(); for (File file : files) { uris.add(Uri.fromFile(file)); @@ -997,7 +966,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter return; } Intent i = new Intent(Intent.ACTION_SEND_MULTIPLE); - i.setType("message/rfc822") ; + i.setType("message/rfc822"); i.putExtra(Intent.EXTRA_EMAIL, new String[]{BuildVars.SEND_LOGS_EMAIL}); i.putExtra(Intent.EXTRA_SUBJECT, "last logs"); i.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris); @@ -1024,7 +993,8 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter return i == textSizeRow || i == enableAnimationsRow || i == notificationRow || i == backgroundRow || i == numberRow || i == askQuestionRow || i == sendLogsRow || i == sendByEnterRow || i == privacyRow || i == wifiDownloadRow || i == mobileDownloadRow || i == clearLogsRow || i == roamingDownloadRow || i == languageRow || i == usernameRow || - i == switchBackendButtonRow || i == telegramFaqRow || i == contactsSortRow || i == contactsReimportRow || i == saveToGalleryRow; + i == switchBackendButtonRow || i == telegramFaqRow || i == contactsSortRow || i == contactsReimportRow || i == saveToGalleryRow || + i == stickersRow; } @Override @@ -1055,9 +1025,9 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter view = new EmptyCell(mContext); } if (i == overscrollRow) { - ((EmptyCell) view).setHeight(88); + ((EmptyCell) view).setHeight(AndroidUtilities.dp(88)); } else { - ((EmptyCell) view).setHeight(16); + ((EmptyCell) view).setHeight(AndroidUtilities.dp(16)); } } else if (type == 1) { if (view == null) { @@ -1104,6 +1074,8 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter textCell.setText(LocaleController.getString("TelegramFAQ", R.string.TelegramFaq), true); } else if (i == contactsReimportRow) { textCell.setText(LocaleController.getString("ImportContacts", R.string.ImportContacts), true); + } else if (i == stickersRow) { + textCell.setText(LocaleController.getString("Stickers", R.string.Stickers), true); } } else if (type == 3) { if (view == null) { @@ -1151,7 +1123,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter TextDetailSettingsCell textCell = (TextDetailSettingsCell) view; if (i == mobileDownloadRow || i == wifiDownloadRow || i == roamingDownloadRow) { - int mask = 0; + int mask; String value; SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); if (i == mobileDownloadRow) { @@ -1217,11 +1189,12 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter public int getItemViewType(int i) { if (i == emptyRow || i == overscrollRow) { return 0; - } if (i == settingsSectionRow || i == supportSectionRow || i == messagesSectionRow || i == mediaDownloadSection || i == contactsSectionRow) { + } + if (i == settingsSectionRow || i == supportSectionRow || i == messagesSectionRow || i == mediaDownloadSection || i == contactsSectionRow) { return 1; } else if (i == enableAnimationsRow || i == sendByEnterRow || i == saveToGalleryRow) { return 3; - } else if (i == notificationRow || i == backgroundRow || i == askQuestionRow || i == sendLogsRow || i == privacyRow || i == clearLogsRow || i == switchBackendButtonRow || i == telegramFaqRow || i == contactsReimportRow || i == textSizeRow || i == languageRow || i == contactsSortRow) { + } else if (i == notificationRow || i == backgroundRow || i == askQuestionRow || i == sendLogsRow || i == privacyRow || i == clearLogsRow || i == switchBackendButtonRow || i == telegramFaqRow || i == contactsReimportRow || i == textSizeRow || i == languageRow || i == contactsSortRow || i == stickersRow) { return 2; } else if (i == versionRow) { return 5; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/StickersActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/StickersActivity.java new file mode 100644 index 000000000..ce76b2d02 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/StickersActivity.java @@ -0,0 +1,316 @@ +/* + * This is the source code of Telegram for Android v. 2.x.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2014. + */ + +package org.telegram.ui; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Build; +import android.os.Message; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.FrameLayout; +import android.widget.ListView; +import android.widget.Toast; + +import org.telegram.android.LocaleController; +import org.telegram.android.MessagesController; +import org.telegram.android.NotificationCenter; +import org.telegram.android.query.StickersQuery; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.R; +import org.telegram.messenger.TLRPC; +import org.telegram.ui.ActionBar.ActionBar; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Adapters.BaseFragmentAdapter; +import org.telegram.ui.Cells.StickerSetCell; +import org.telegram.ui.Cells.TextInfoPrivacyCell; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.StickersAlert; +import org.telegram.ui.Components.URLSpanNoUnderline; + +import java.util.ArrayList; +import java.util.Locale; + +public class StickersActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { + + private ListAdapter listAdapter; + + private int stickersStartRow; + private int stickersEndRow; + private int stickersInfoRow; + private int rowCount; + + @Override + public boolean onFragmentCreate() { + super.onFragmentCreate(); + StickersQuery.checkStickers(); + NotificationCenter.getInstance().addObserver(this, NotificationCenter.stickersDidLoaded); + updateRows(); + return true; + } + + @Override + public void onFragmentDestroy() { + super.onFragmentDestroy(); + NotificationCenter.getInstance().removeObserver(this, NotificationCenter.stickersDidLoaded); + } + + @Override + public View createView(Context context, LayoutInflater inflater) { + actionBar.setBackButtonImage(R.drawable.ic_ab_back); + actionBar.setAllowOverlayTitle(true); + actionBar.setTitle(LocaleController.getString("Stickers", R.string.Stickers)); + actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { + @Override + public void onItemClick(int id) { + if (id == -1) { + finishFragment(); + } + } + }); + + listAdapter = new ListAdapter(context); + + fragmentView = new FrameLayout(context); + FrameLayout frameLayout = (FrameLayout) fragmentView; + frameLayout.setBackgroundColor(0xfff0f0f0); + + ListView listView = new ListView(context); + listView.setDivider(null); + listView.setDividerHeight(0); + listView.setVerticalScrollBarEnabled(false); + listView.setDrawSelectorOnTop(true); + frameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + listView.setAdapter(listAdapter); + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView adapterView, View view, final int i, long l) { + if (i >= stickersStartRow && i < stickersEndRow && getParentActivity() != null) { + final TLRPC.TL_stickerSet stickerSet = StickersQuery.getStickerSets().get(i); + ArrayList stickers = StickersQuery.getStickersForSet(stickerSet.id); + if (stickers == null) { + return; + } + StickersAlert alert = new StickersAlert(getParentActivity(), stickerSet, stickers); + alert.setButton(AlertDialog.BUTTON_NEGATIVE, LocaleController.getString("Close", R.string.Close), (Message) null); + if (stickerSet.id != -1) { + alert.setButton(AlertDialog.BUTTON_NEUTRAL, LocaleController.getString("StickersRemove", R.string.StickersRemove), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + StickersQuery.removeStickersSet(getParentActivity(), stickerSet); + } + }); + } + setVisibleDialog(alert); + alert.show(); + } + } + }); + + return fragmentView; + } + + @Override + public void didReceivedNotification(int id, Object... args) { + if (id == NotificationCenter.stickersDidLoaded) { + updateRows(); + } + } + + private void updateRows() { + rowCount = 0; + ArrayList stickerSets = StickersQuery.getStickerSets(); + if (!stickerSets.isEmpty()) { + stickersStartRow = 0; + stickersEndRow = stickerSets.size(); + rowCount += stickerSets.size(); + } else { + stickersStartRow = -1; + stickersEndRow = -1; + } + stickersInfoRow = rowCount++; + if (listAdapter != null) { + listAdapter.notifyDataSetChanged(); + } + } + + @Override + public void onResume() { + super.onResume(); + if (listAdapter != null) { + listAdapter.notifyDataSetChanged(); + } + } + + private class ListAdapter extends BaseFragmentAdapter { + private Context mContext; + + public ListAdapter(Context context) { + mContext = context; + } + + @Override + public boolean areAllItemsEnabled() { + return false; + } + + @Override + public boolean isEnabled(int i) { + return i >= stickersStartRow && i < stickersEndRow; + } + + @Override + public int getCount() { + return rowCount; + } + + @Override + public Object getItem(int i) { + return null; + } + + @Override + public long getItemId(int i) { + return i; + } + + @Override + public boolean hasStableIds() { + return false; + } + + @Override + public View getView(int i, View view, ViewGroup viewGroup) { + int type = getItemViewType(i); + if (type == 0) { + if (view == null) { + view = new StickerSetCell(mContext); + view.setBackgroundColor(0xffffffff); + ((StickerSetCell) view).setOnOptionsClick(new View.OnClickListener() { + @Override + public void onClick(View v) { + StickerSetCell cell = (StickerSetCell) v.getParent(); + final TLRPC.TL_stickerSet stickerSet = cell.getStickersSet(); + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + CharSequence[] items; + if (stickerSet.id == -1) { + builder.setTitle(LocaleController.getString("GeniusStickerPackName", R.string.GeniusStickerPackName)); + items = new CharSequence[]{ + StickersQuery.getHideMainStickersPack() ? LocaleController.getString("StickersShow", R.string.StickersShow) : LocaleController.getString("StickersHide", R.string.StickersHide) + }; + } else { + builder.setTitle(stickerSet.title); + items = new CharSequence[]{ + LocaleController.getString("StickersRemove", R.string.StickersRemove), + LocaleController.getString("StickersShare", R.string.StickersShare), + LocaleController.getString("StickersCopy", R.string.StickersCopy), + }; + } + builder.setItems(items, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (which == 0) { + if (stickerSet.id == -1) { + StickersQuery.setHideMainStickersPack(!StickersQuery.getHideMainStickersPack()); + listAdapter.notifyDataSetChanged(); + StickersQuery.loadStickers(true, false); + } else { + StickersQuery.removeStickersSet(getParentActivity(), stickerSet); + } + } else if (which == 1) { + try { + Intent intent = new Intent(Intent.ACTION_SEND); + intent.setType("text/plain"); + intent.putExtra(Intent.EXTRA_TEXT, String.format(Locale.US, "https://telegram.me/addstickers/%s", stickerSet.short_name)); + getParentActivity().startActivityForResult(Intent.createChooser(intent, LocaleController.getString("StickersShare", R.string.StickersShare)), 500); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } else if (which == 2) { + try { + if (Build.VERSION.SDK_INT < 11) { + android.text.ClipboardManager clipboard = (android.text.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE); + clipboard.setText(String.format(Locale.US, "https://telegram.me/addstickers/%s", stickerSet.short_name)); + } else { + android.content.ClipboardManager clipboard = (android.content.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE); + android.content.ClipData clip = android.content.ClipData.newPlainText("label", String.format(Locale.US, "https://telegram.me/addstickers/%s", stickerSet.short_name)); + clipboard.setPrimaryClip(clip); + } + Toast.makeText(getParentActivity(), LocaleController.getString("LinkCopied", R.string.LinkCopied), Toast.LENGTH_SHORT).show(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + } + }); + //builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + showDialog(builder.create()); + } + }); + } + ArrayList arrayList = StickersQuery.getStickerSets(); + ((StickerSetCell) view).setStickersSet(arrayList.get(i), i != arrayList.size() - 1); + } else if (type == 1) { + if (view == null) { + view = new TextInfoPrivacyCell(mContext); + String text = LocaleController.getString("StickersInfo", R.string.StickersInfo); + String botName = "@stickers"; + int index = text.indexOf(botName); + if (index != -1) { + try { + SpannableStringBuilder stringBuilder = new SpannableStringBuilder(text); + URLSpanNoUnderline spanNoUnderline = new URLSpanNoUnderline("@stickers") { + @Override + public void onClick(View widget) { + MessagesController.openByUserName("stickers", StickersActivity.this, 1); + } + }; + stringBuilder.setSpan(spanNoUnderline, index, index + botName.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); + ((TextInfoPrivacyCell) view).setText(stringBuilder); + } catch (Exception e) { + FileLog.e("tmessages", e); + ((TextInfoPrivacyCell) view).setText(text); + } + } else { + ((TextInfoPrivacyCell) view).setText(text); + } + view.setBackgroundResource(R.drawable.greydivider_bottom); + } + } + return view; + } + + @Override + public int getItemViewType(int i) { + if (i >= stickersStartRow && i < stickersEndRow) { + return 0; + } else if (i == stickersInfoRow) { + return 1; + } + return 0; + } + + @Override + public int getViewTypeCount() { + return 2; + } + + @Override + public boolean isEmpty() { + return false; + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/TwoStepVerificationActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/TwoStepVerificationActivity.java index 74f7e9de3..079bf6926 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/TwoStepVerificationActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/TwoStepVerificationActivity.java @@ -9,6 +9,7 @@ package org.telegram.ui; import android.app.AlertDialog; +import android.app.Dialog; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; @@ -56,6 +57,7 @@ import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.Adapters.BaseFragmentAdapter; import org.telegram.ui.Cells.TextInfoPrivacyCell; import org.telegram.ui.Cells.TextSettingsCell; +import org.telegram.ui.Components.LayoutHelper; public class TwoStepVerificationActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { @@ -167,8 +169,8 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific scrollView.setFillViewport(true); frameLayout.addView(scrollView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) scrollView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; scrollView.setLayoutParams(layoutParams); LinearLayout linearLayout = new LinearLayout(context); @@ -185,8 +187,8 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific titleTextView.setGravity(Gravity.CENTER_HORIZONTAL); linearLayout.addView(titleTextView); LinearLayout.LayoutParams layoutParams3 = (LinearLayout.LayoutParams) titleTextView.getLayoutParams(); - layoutParams3.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams3.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams3.width = LayoutHelper.WRAP_CONTENT; + layoutParams3.height = LayoutHelper.WRAP_CONTENT; layoutParams3.gravity = Gravity.CENTER_HORIZONTAL; layoutParams3.topMargin = AndroidUtilities.dp(38); titleTextView.setLayoutParams(layoutParams3); @@ -209,7 +211,7 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific layoutParams3.leftMargin = AndroidUtilities.dp(40); layoutParams3.rightMargin = AndroidUtilities.dp(40); layoutParams3.gravity = Gravity.TOP | Gravity.LEFT; - layoutParams3.width = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams3.width = LayoutHelper.MATCH_PARENT; passwordEditText.setLayoutParams(layoutParams3); passwordEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override @@ -253,8 +255,8 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific bottomTextView.setText(LocaleController.getString("YourEmailInfo", R.string.YourEmailInfo)); linearLayout.addView(bottomTextView); layoutParams3 = (LinearLayout.LayoutParams) bottomTextView.getLayoutParams(); - layoutParams3.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams3.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams3.width = LayoutHelper.WRAP_CONTENT; + layoutParams3.height = LayoutHelper.WRAP_CONTENT; layoutParams3.gravity = (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP; layoutParams3.topMargin = AndroidUtilities.dp(30); layoutParams3.leftMargin = AndroidUtilities.dp(40); @@ -265,8 +267,8 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific linearLayout2.setGravity(Gravity.BOTTOM | Gravity.CENTER_VERTICAL); linearLayout.addView(linearLayout2); layoutParams3 = (LinearLayout.LayoutParams) linearLayout2.getLayoutParams(); - layoutParams3.width = LinearLayout.LayoutParams.MATCH_PARENT; - layoutParams3.height = LinearLayout.LayoutParams.MATCH_PARENT; + layoutParams3.width = LayoutHelper.MATCH_PARENT; + layoutParams3.height = LayoutHelper.MATCH_PARENT; linearLayout2.setLayoutParams(layoutParams3); bottomButton = new TextView(context); @@ -277,8 +279,8 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific bottomButton.setPadding(0, AndroidUtilities.dp(10), 0, 0); linearLayout2.addView(bottomButton); layoutParams3 = (LinearLayout.LayoutParams) bottomButton.getLayoutParams(); - layoutParams3.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams3.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams3.width = LayoutHelper.WRAP_CONTENT; + layoutParams3.height = LayoutHelper.WRAP_CONTENT; layoutParams3.gravity = (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.BOTTOM; layoutParams3.bottomMargin = AndroidUtilities.dp(14); layoutParams3.leftMargin = AndroidUtilities.dp(40); @@ -313,7 +315,7 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific presentFragment(fragment); } }); - AlertDialog dialog = showAlertDialog(builder); + Dialog dialog = showDialog(builder.create()); if (dialog != null) { dialog.setCanceledOnTouchOutside(false); dialog.setCancelable(false); @@ -354,7 +356,7 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } } } @@ -364,8 +366,8 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific progressView = new FrameLayout(context); frameLayout.addView(progressView); layoutParams = (FrameLayout.LayoutParams) progressView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; progressView.setLayoutParams(layoutParams); progressView.setOnTouchListener(new View.OnTouchListener() { @Override @@ -377,8 +379,8 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific ProgressBar progressBar = new ProgressBar(context); progressView.addView(progressBar); layoutParams = (FrameLayout.LayoutParams) progressView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; + layoutParams.width = LayoutHelper.WRAP_CONTENT; + layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.gravity = Gravity.CENTER; progressView.setLayoutParams(layoutParams); @@ -390,8 +392,8 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific listView.setDrawSelectorOnTop(true); frameLayout.addView(listView); layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; - layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP; listView.setLayoutParams(layoutParams); listView.setAdapter(listAdapter = new ListAdapter(context)); @@ -421,7 +423,7 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showDialog(builder.create()); } } }); @@ -440,7 +442,7 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific @Override public void didReceivedNotification(int id, Object... args) { if (id == NotificationCenter.didSetTwoStepPassword) { - if (args != null && args.length > 0) { + if (args != null && args.length > 0 && args[0] != null) { currentPasswordHash = (byte[]) args[0]; } loadPasswordInfo(false); @@ -695,7 +697,7 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null); builder.setTitle(title); builder.setMessage(text); - showAlertDialog(builder); + showDialog(builder.create()); } private void setNewPassword(final boolean clear) { @@ -706,6 +708,7 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific if (waitingForEmail && currentPassword instanceof TLRPC.TL_account_noPassword) { req.new_settings.flags = 2; req.new_settings.email = ""; + req.current_password_hash = new byte[0]; } else { req.new_settings.flags = 3; req.new_settings.hint = ""; @@ -765,7 +768,7 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific }); builder.setMessage(LocaleController.getString("YourPasswordSuccessText", R.string.YourPasswordSuccessText)); builder.setTitle(LocaleController.getString("YourPasswordSuccess", R.string.YourPasswordSuccess)); - AlertDialog dialog = showAlertDialog(builder); + Dialog dialog = showDialog(builder.create()); if (dialog != null) { dialog.setCanceledOnTouchOutside(false); dialog.setCancelable(false); @@ -783,7 +786,7 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific }); builder.setMessage(LocaleController.getString("YourEmailAlmostThereText", R.string.YourEmailAlmostThereText)); builder.setTitle(LocaleController.getString("YourEmailAlmostThere", R.string.YourEmailAlmostThere)); - AlertDialog dialog = showAlertDialog(builder); + Dialog dialog = showDialog(builder.create()); if (dialog != null) { dialog.setCanceledOnTouchOutside(false); dialog.setCancelable(false); @@ -936,7 +939,7 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific }); builder.setMessage(LocaleController.getString("PasswordReset", R.string.PasswordReset)); builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); - AlertDialog dialog = showAlertDialog(builder); + Dialog dialog = showDialog(builder.create()); if (dialog != null) { dialog.setCanceledOnTouchOutside(false); dialog.setCancelable(false); @@ -993,7 +996,7 @@ public class TwoStepVerificationActivity extends BaseFragment implements Notific @Override public boolean isEnabled(int i) { - return i != setPasswordDetailRow && i != shadowRow && i != passwordSetupDetailRow && i != passwordEmailVerifyDetailRow; + return i != setPasswordDetailRow && i != shadowRow && i != passwordSetupDetailRow && i != passwordEmailVerifyDetailRow && i != passwordEnabledDetailRow; } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/VideoEditorActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/VideoEditorActivity.java index 7121d0a42..37fa05bf4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/VideoEditorActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/VideoEditorActivity.java @@ -47,10 +47,10 @@ import org.telegram.android.NotificationCenter; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.FileLog; import org.telegram.messenger.R; -import org.telegram.messenger.Utilities; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.VideoSeekBarView; import org.telegram.ui.Components.VideoTimelineView; @@ -104,7 +104,7 @@ public class VideoEditorActivity extends BaseFragment implements TextureView.Sur private Runnable progressRunnable = new Runnable() { @Override public void run() { - boolean playerCheck = false; + boolean playerCheck; while (true) { synchronized (sync) { @@ -490,7 +490,7 @@ public class VideoEditorActivity extends BaseFragment implements TextureView.Sur long duration = (long)Math.ceil(videoDuration); int minutes = (int)(duration / 1000 / 60); int seconds = (int) Math.ceil(duration / 1000) - minutes * 60; - String videoTimeSize = String.format("%d:%02d, %s", minutes, seconds, Utilities.formatFileSize(originalSize)); + String videoTimeSize = String.format("%d:%02d, %s", minutes, seconds, AndroidUtilities.formatFileSize(originalSize)); originalSizeTextView.setText(String.format("%s, %s", videoDimension, videoTimeSize)); } @@ -500,8 +500,8 @@ public class VideoEditorActivity extends BaseFragment implements TextureView.Sur } esimatedDuration = (long)Math.ceil((videoTimelineView.getRightProgress() - videoTimelineView.getLeftProgress()) * videoDuration); - int width = 0; - int height = 0; + int width; + int height; if (compressVideo.getVisibility() == View.GONE || compressVideo.getVisibility() == View.VISIBLE && !compressVideo.isChecked()) { width = rotationValue == 90 || rotationValue == 270 ? originalHeight : originalWidth; @@ -527,7 +527,7 @@ public class VideoEditorActivity extends BaseFragment implements TextureView.Sur String videoDimension = String.format("%dx%d", width, height); int minutes = (int)(esimatedDuration / 1000 / 60); int seconds = (int) Math.ceil(esimatedDuration / 1000) - minutes * 60; - String videoTimeSize = String.format("%d:%02d, ~%s", minutes, seconds, Utilities.formatFileSize(estimatedSize)); + String videoTimeSize = String.format("%d:%02d, ~%s", minutes, seconds, AndroidUtilities.formatFileSize(estimatedSize)); editedSizeTextView.setText(String.format("%s, %s", videoDimension, videoTimeSize)); } @@ -535,15 +535,15 @@ public class VideoEditorActivity extends BaseFragment implements TextureView.Sur if (fragmentView == null || getParentActivity() == null) { return; } - int viewHeight = 0; + int viewHeight; if (AndroidUtilities.isTablet()) { viewHeight = AndroidUtilities.dp(472); } else { viewHeight = AndroidUtilities.displaySize.y - AndroidUtilities.statusBarHeight - AndroidUtilities.getCurrentActionBarHeight(); } - int width = 0; - int height = 0; + int width; + int height; if (AndroidUtilities.isTablet()) { width = AndroidUtilities.dp(490); height = viewHeight - AndroidUtilities.dp(276 + (compressVideo.getVisibility() == View.VISIBLE ? 20 : 0)); @@ -611,7 +611,7 @@ public class VideoEditorActivity extends BaseFragment implements TextureView.Sur FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) videoContainerView.getLayoutParams(); layoutParams.topMargin = AndroidUtilities.dp(16); layoutParams.bottomMargin = AndroidUtilities.dp(260 + (compressVideo.getVisibility() == View.VISIBLE ? 20 : 0)); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.leftMargin = 0; videoContainerView.setLayoutParams(layoutParams); @@ -619,12 +619,12 @@ public class VideoEditorActivity extends BaseFragment implements TextureView.Sur layoutParams.topMargin = 0; layoutParams.leftMargin = 0; layoutParams.bottomMargin = AndroidUtilities.dp(150 + (compressVideo.getVisibility() == View.VISIBLE ? 20 : 0)); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.BOTTOM; controlView.setLayoutParams(layoutParams); layoutParams = (FrameLayout.LayoutParams) textContainerView.getLayoutParams(); - layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; + layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.leftMargin = AndroidUtilities.dp(16); layoutParams.rightMargin = AndroidUtilities.dp(16); layoutParams.bottomMargin = AndroidUtilities.dp(16); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/WallpapersActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/WallpapersActivity.java index ab3380f21..1689a0e62 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/WallpapersActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/WallpapersActivity.java @@ -14,24 +14,26 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; -import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Point; -import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.provider.MediaStore; +import android.view.Gravity; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.ViewTreeObserver; -import android.widget.AdapterView; +import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ProgressBar; import org.telegram.android.AndroidUtilities; import org.telegram.android.ImageLoader; import org.telegram.android.LocaleController; +import org.telegram.android.support.widget.LinearLayoutManager; +import org.telegram.android.support.widget.RecyclerView; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.TLObject; import org.telegram.messenger.TLRPC; @@ -42,13 +44,13 @@ import org.telegram.android.MessagesStorage; import org.telegram.android.NotificationCenter; import org.telegram.messenger.R; import org.telegram.messenger.RPCRequest; -import org.telegram.messenger.Utilities; -import org.telegram.ui.Adapters.BaseFragmentAdapter; + import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; -import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Cells.WallpaperCell; import org.telegram.ui.ActionBar.BaseFragment; -import org.telegram.ui.Components.HorizontalListView; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.RecyclerListView; import java.io.File; import java.io.FileOutputStream; @@ -57,7 +59,6 @@ import java.util.HashMap; public class WallpapersActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { - private HorizontalListView listView; private ListAdapter listAdapter; private ImageView backgroundImage; private ProgressBar progressBar; @@ -126,7 +127,7 @@ public class WallpapersActivity extends BaseFragment implements NotificationCent File f = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName); File toFile = new File(ApplicationLoader.applicationContext.getFilesDir(), "wallpaper.jpg"); try { - done = Utilities.copyFile(f, toFile); + done = AndroidUtilities.copyFile(f, toFile); } catch (Exception e) { done = false; FileLog.e("tmessages", e); @@ -157,17 +158,40 @@ public class WallpapersActivity extends BaseFragment implements NotificationCent ActionBarMenu menu = actionBar.createMenu(); doneButton = menu.addItemWithWidth(done_button, R.drawable.ic_done, AndroidUtilities.dp(56)); - fragmentView = inflater.inflate(R.layout.settings_wallpapers_layout, null, false); - listAdapter = new ListAdapter(context); + FrameLayout frameLayout = new FrameLayout(context); + fragmentView = frameLayout; - progressBar = (ProgressBar) fragmentView.findViewById(R.id.action_progress); - backgroundImage = (ImageView) fragmentView.findViewById(R.id.background_image); - listView = (HorizontalListView) fragmentView.findViewById(R.id.listView); - listView.setAdapter(listAdapter); - listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + backgroundImage = new ImageView(context); + backgroundImage.setScaleType(ImageView.ScaleType.CENTER_CROP); + frameLayout.addView(backgroundImage, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + backgroundImage.setOnTouchListener(new View.OnTouchListener() { @Override - public void onItemClick(AdapterView adapterView, View view, int i, long l) { - if (i == 0) { + public boolean onTouch(View v, MotionEvent event) { + return true; + } + }); + + progressBar = new ProgressBar(context); + progressBar.setPadding(AndroidUtilities.dp(6), AndroidUtilities.dp(6), AndroidUtilities.dp(6), AndroidUtilities.dp(6)); + frameLayout.addView(progressBar, LayoutHelper.createFrame(60, 60, Gravity.CENTER, 0, 0, 0, 52)); + + RecyclerListView listView = new RecyclerListView(context); + listView.setClipToPadding(false); + listView.setPadding(AndroidUtilities.dp(40), 0, AndroidUtilities.dp(40), 0); + LinearLayoutManager layoutManager = new LinearLayoutManager(context); + layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); + listView.setLayoutManager(layoutManager); + listView.setClipToPadding(false); + listView.setDisallowInterceptTouchEvents(true); + if (Build.VERSION.SDK_INT >= 9) { + listView.setOverScrollMode(RecyclerListView.OVER_SCROLL_NEVER); + } + listView.setAdapter(listAdapter = new ListAdapter(context)); + frameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 102, Gravity.LEFT | Gravity.BOTTOM)); + listView.setOnItemClickListener(new RecyclerListView.OnItemClickListener() { + @Override + public void onItemClick(View view, int position) { + if (position == 0) { if (getParentActivity() == null) { return; } @@ -181,7 +205,7 @@ public class WallpapersActivity extends BaseFragment implements NotificationCent try { if (i == 0) { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - File image = Utilities.generatePicturePath(); + File image = AndroidUtilities.generatePicturePath(); if (image != null) { takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(image)); currentPicturePath = image.getAbsolutePath(); @@ -197,9 +221,12 @@ public class WallpapersActivity extends BaseFragment implements NotificationCent } } }); - showAlertDialog(builder); + showDialog(builder.create()); } else { - TLRPC.WallPaper wallPaper = wallPapers.get(i - 1); + if (position - 1 < 0 || position - 1 >= wallPapers.size()) { + return; + } + TLRPC.WallPaper wallPaper = wallPapers.get(position - 1); selectedBackground = wallPaper.id; listAdapter.notifyDataSetChanged(); processSelectedBackground(); @@ -216,7 +243,7 @@ public class WallpapersActivity extends BaseFragment implements NotificationCent public void onActivityResultFragment(int requestCode, int resultCode, Intent data) { if (resultCode == Activity.RESULT_OK) { if (requestCode == 10) { - Utilities.addMediaToGallery(currentPicturePath); + AndroidUtilities.addMediaToGallery(currentPicturePath); FileOutputStream stream = null; try { Point screenSize = AndroidUtilities.getRealScreenSize(); @@ -292,7 +319,7 @@ public class WallpapersActivity extends BaseFragment implements NotificationCent progressBar.setVisibility(View.VISIBLE); loadingSize = size; selectedColor = 0; - FileLoader.getInstance().loadFile(size, true); + FileLoader.getInstance().loadFile(size, null, true); backgroundImage.setBackgroundColor(0); } else { if (loadingFile != null) { @@ -352,7 +379,7 @@ public class WallpapersActivity extends BaseFragment implements NotificationCent @Override public void didReceivedNotification(int id, final Object... args) { if (id == NotificationCenter.FileDidFailedLoad) { - String location = (String)args[0]; + String location = (String) args[0]; if (loadingFile != null && loadingFile.equals(location)) { loadingFileObject = null; loadingFile = null; @@ -361,7 +388,7 @@ public class WallpapersActivity extends BaseFragment implements NotificationCent doneButton.setEnabled(false); } } else if (id == NotificationCenter.FileDidLoaded) { - String location = (String)args[0]; + String location = (String) args[0]; if (loadingFile != null && loadingFile.equals(location)) { backgroundImage.setImageURI(Uri.fromFile(loadingFileObject)); progressBar.setVisibility(View.GONE); @@ -372,38 +399,27 @@ public class WallpapersActivity extends BaseFragment implements NotificationCent loadingSize = null; } } else if (id == NotificationCenter.FileLoadProgressChanged) { - String location = (String)args[0]; + String location = (String) args[0]; if (loadingFile != null && loadingFile.equals(location)) { - Float progress = (Float)args[1]; - progressBar.setProgress((int)(progress * 100)); + Float progress = (Float) args[1]; + progressBar.setProgress((int) (progress * 100)); } } else if (id == NotificationCenter.wallpapersDidLoaded) { - AndroidUtilities.runOnUIThread(new Runnable() { - @Override - public void run() { - wallPapers = (ArrayList)args[0]; - wallpappersByIds.clear(); - for (TLRPC.WallPaper wallPaper : wallPapers) { - wallpappersByIds.put(wallPaper.id, wallPaper); - } - if (listAdapter != null) { - listAdapter.notifyDataSetChanged(); - } - if (!wallPapers.isEmpty() && backgroundImage != null) { - processSelectedBackground(); - } - loadWallpapers(); - } - }); + wallPapers = (ArrayList) args[0]; + wallpappersByIds.clear(); + for (TLRPC.WallPaper wallPaper : wallPapers) { + wallpappersByIds.put(wallPaper.id, wallPaper); + } + if (listAdapter != null) { + listAdapter.notifyDataSetChanged(); + } + if (!wallPapers.isEmpty() && backgroundImage != null) { + processSelectedBackground(); + } + loadWallpapers(); } } - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - fixLayout(); - } - private void loadWallpapers() { TLRPC.TL_account_getWallPapers req = new TLRPC.TL_account_getWallPapers(); long reqId = ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() { @@ -416,11 +432,11 @@ public class WallpapersActivity extends BaseFragment implements NotificationCent @Override public void run() { wallPapers.clear(); - TLRPC.Vector res = (TLRPC.Vector)response; + TLRPC.Vector res = (TLRPC.Vector) response; wallpappersByIds.clear(); for (Object obj : res.objects) { - wallPapers.add((TLRPC.WallPaper)obj); - wallpappersByIds.put(((TLRPC.WallPaper)obj).id, (TLRPC.WallPaper)obj); + wallPapers.add((TLRPC.WallPaper) obj); + wallpappersByIds.put(((TLRPC.WallPaper) obj).id, (TLRPC.WallPaper) obj); } if (listAdapter != null) { listAdapter.notifyDataSetChanged(); @@ -436,28 +452,6 @@ public class WallpapersActivity extends BaseFragment implements NotificationCent ConnectionsManager.getInstance().bindRequestToGuid(reqId, classGuid); } - private void fixLayout() { - ViewTreeObserver obs = fragmentView.getViewTreeObserver(); - obs.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - fragmentView.getViewTreeObserver().removeOnPreDrawListener(this); - if (listAdapter != null) { - listAdapter.notifyDataSetChanged(); - } - if (listView != null) { - listView.post(new Runnable() { - @Override - public void run() { - listView.scrollTo(0); - } - }); - } - return false; - } - }); - } - @Override public void onResume() { super.onResume(); @@ -465,10 +459,17 @@ public class WallpapersActivity extends BaseFragment implements NotificationCent listAdapter.notifyDataSetChanged(); } processSelectedBackground(); - fixLayout(); } - private class ListAdapter extends BaseFragmentAdapter { + private class ListAdapter extends RecyclerView.Adapter { + + private class Holder extends RecyclerView.ViewHolder { + + public Holder(View itemView) { + super(itemView); + } + } + private Context mContext; public ListAdapter(Context context) { @@ -476,109 +477,24 @@ public class WallpapersActivity extends BaseFragment implements NotificationCent } @Override - public boolean areAllItemsEnabled() { - return false; - } - - @Override - public boolean isEnabled(int i) { - return true; - } - - @Override - public int getCount() { + public int getItemCount() { return 1 + wallPapers.size(); } - @Override - public Object getItem(int i) { - return null; - } - @Override public long getItemId(int i) { return i; } @Override - public boolean hasStableIds() { - return true; + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { + WallpaperCell view = new WallpaperCell(mContext); + return new Holder(view); } @Override - public View getView(int i, View view, ViewGroup viewGroup) { - int type = getItemViewType(i); - if (type == 0) { - if (view == null) { - LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - view = li.inflate(R.layout.settings_wallpapers_my_row, viewGroup, false); - } - View parentView = view.findViewById(R.id.parent); - ImageView imageView = (ImageView)view.findViewById(R.id.image); - View selection = view.findViewById(R.id.selection); - if (i == 0) { - if (selectedBackground == -1 || selectedColor != 0 || selectedBackground == 1000001) { - imageView.setBackgroundColor(0x5A475866); - } else { - imageView.setBackgroundColor(0x5A000000); - } - imageView.setImageResource(R.drawable.ic_gallery_background); - if (selectedBackground == -1) { - selection.setVisibility(View.VISIBLE); - } else { - selection.setVisibility(View.INVISIBLE); - } - } else { - imageView.setImageBitmap(null); - TLRPC.WallPaper wallPaper = wallPapers.get(i - 1); - imageView.setBackgroundColor(0xff000000 | wallPaper.bg_color); - if (wallPaper.id == selectedBackground) { - selection.setVisibility(View.VISIBLE); - } else { - selection.setVisibility(View.INVISIBLE); - } - } - } else if (type == 1) { - if (view == null) { - LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - view = li.inflate(R.layout.settings_wallpapers_other_row, viewGroup, false); - } - BackupImageView image = (BackupImageView)view.findViewById(R.id.image); - View selection = view.findViewById(R.id.selection); - TLRPC.WallPaper wallPaper = wallPapers.get(i - 1); - TLRPC.PhotoSize size = FileLoader.getClosestPhotoSizeWithSize(wallPaper.sizes, AndroidUtilities.dp(100)); - if (size != null && size.location != null) { - image.setImage(size.location, "100_100", (Drawable)null); - } - if (wallPaper.id == selectedBackground) { - selection.setVisibility(View.VISIBLE); - } else { - selection.setVisibility(View.INVISIBLE); - } - } - return view; - } - - @Override - public int getItemViewType(int i) { - if (i == 0) { - return 0; - } - TLRPC.WallPaper wallPaper = wallPapers.get(i - 1); - if (wallPaper instanceof TLRPC.TL_wallPaperSolid) { - return 0; - } - return 1; - } - - @Override - public int getViewTypeCount() { - return 2; - } - - @Override - public boolean isEmpty() { - return false; + public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) { + ((WallpaperCell) viewHolder.itemView).setWallpaper(i == 0 ? null : wallPapers.get(i - 1), selectedBackground); } } } diff --git a/TMessagesProj/src/main/res/drawable-hdpi/addmember.png b/TMessagesProj/src/main/res/drawable-hdpi/addmember.png new file mode 100755 index 000000000..166473d7b Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/addmember.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/arrow_down_w.png b/TMessagesProj/src/main/res/drawable-hdpi/arrow_down_w.png new file mode 100755 index 000000000..deca1e6e9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/arrow_down_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/audiocancel1.png b/TMessagesProj/src/main/res/drawable-hdpi/audiocancel1.png deleted file mode 100755 index 7f5b68969..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/audiocancel1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/audiocancel1_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/audiocancel1_pressed.png deleted file mode 100755 index 8963b7120..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/audiocancel1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/audiocancel2.png b/TMessagesProj/src/main/res/drawable-hdpi/audiocancel2.png deleted file mode 100755 index aad25e8ed..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/audiocancel2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/audiocancel2_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/audiocancel2_pressed.png deleted file mode 100755 index 9bdc31ef1..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/audiocancel2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/audioload1.png b/TMessagesProj/src/main/res/drawable-hdpi/audioload1.png deleted file mode 100755 index 1eecb03db..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/audioload1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/audioload1_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/audioload1_pressed.png deleted file mode 100755 index 854fe27a3..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/audioload1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/audioload2.png b/TMessagesProj/src/main/res/drawable-hdpi/audioload2.png deleted file mode 100755 index 9dc1787a7..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/audioload2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/audioload2_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/audioload2_pressed.png deleted file mode 100755 index feab040a6..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/audioload2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/bg_emoji_bs.9.png b/TMessagesProj/src/main/res/drawable-hdpi/bg_emoji_bs.9.png deleted file mode 100644 index a90f085cb..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/bg_emoji_bs.9.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/cancel_b.png b/TMessagesProj/src/main/res/drawable-hdpi/cancel_b.png new file mode 100755 index 000000000..c942d603c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/cancel_b.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/cancel_b_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/cancel_b_pressed.png new file mode 100755 index 000000000..58add0c14 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/cancel_b_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/cancel_g.png b/TMessagesProj/src/main/res/drawable-hdpi/cancel_g.png new file mode 100755 index 000000000..7c9c3decb Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/cancel_g.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/cancel_g_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/cancel_g_pressed.png new file mode 100755 index 000000000..2efb6df04 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/cancel_g_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/download_b.png b/TMessagesProj/src/main/res/drawable-hdpi/download_b.png new file mode 100755 index 000000000..a9f428125 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/download_b.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/download_b_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/download_b_pressed.png new file mode 100755 index 000000000..d3a6eb918 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/download_b_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/download_g.png b/TMessagesProj/src/main/res/drawable-hdpi/download_g.png new file mode 100755 index 000000000..1c5762132 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/download_g.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/download_g_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/download_g_pressed.png new file mode 100755 index 000000000..c4bba83d5 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/download_g_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/foursquare.png b/TMessagesProj/src/main/res/drawable-hdpi/foursquare.png new file mode 100644 index 000000000..64783f7ec Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/foursquare.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/header_shadow_reverse.png b/TMessagesProj/src/main/res/drawable-hdpi/header_shadow_reverse.png new file mode 100644 index 000000000..57ccb3eb2 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/header_shadow_reverse.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_ab_location.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_ab_location.png deleted file mode 100755 index 9f11eace7..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_ab_location.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_keyboard_w.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_keyboard_w.png new file mode 100755 index 000000000..f0b6a5da5 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/ic_keyboard_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smile_w.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smile_w.png new file mode 100755 index 000000000..2f8711cd9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smile_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_backspace.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_backspace.png old mode 100644 new mode 100755 index 917303959..50c5312f2 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_backspace.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_backspace.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_backspace_active.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_backspace_active.png old mode 100644 new mode 100755 index 2fa818795..b02ed7c29 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_backspace_active.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_backspace_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_bell.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_bell.png old mode 100644 new mode 100755 index d018c1739..a7a2cffb3 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_bell.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_bell.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_bell_active.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_bell_active.png old mode 100644 new mode 100755 index 98c7f66ca..14cc9aedc Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_bell_active.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_bell_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_car.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_car.png old mode 100644 new mode 100755 index 449b32b47..b8fa468aa Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_car.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_car.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_car_active.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_car_active.png old mode 100644 new mode 100755 index 3f03ef735..7436b5603 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_car_active.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_car_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_flower.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_flower.png old mode 100644 new mode 100755 index 8f190cb4d..b70bcd452 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_flower.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_flower.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_flower_active.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_flower_active.png old mode 100644 new mode 100755 index ca9dd9748..bd783f405 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_flower_active.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_flower_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_grid.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_grid.png old mode 100644 new mode 100755 index ed02b9a1e..f311de53e Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_grid.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_grid.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_grid_active.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_grid_active.png old mode 100644 new mode 100755 index 4b1f86867..2d020da1f Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_grid_active.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_grid_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_recent.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_recent.png old mode 100644 new mode 100755 index 6d032d62b..3208bcecc Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_recent.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_recent.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_recent_active.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_recent_active.png old mode 100644 new mode 100755 index 756df9196..bbf721b80 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_recent_active.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_recent_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_smile.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_smile.png old mode 100644 new mode 100755 index 70ee89c2b..2d98101f8 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_smile.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_smile.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_smile_active.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_smile_active.png old mode 100644 new mode 100755 index 078f863b4..d37ab0982 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_smile_active.png and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_smile_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_sticker.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_sticker.png new file mode 100755 index 000000000..b6f0f7fa0 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_sticker.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_sticker_active.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_sticker_active.png new file mode 100755 index 000000000..6fa2547d7 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/ic_smiles_sticker_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/location_b.9.png b/TMessagesProj/src/main/res/drawable-hdpi/location_b.9.png new file mode 100644 index 000000000..84e92b27b Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/location_b.9.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/location_g.9.png b/TMessagesProj/src/main/res/drawable-hdpi/location_g.9.png new file mode 100644 index 000000000..2c9c70da2 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/location_g.9.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/myloc_on.png b/TMessagesProj/src/main/res/drawable-hdpi/myloc_on.png new file mode 100644 index 000000000..0e93b8072 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/myloc_on.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/navigate.png b/TMessagesProj/src/main/res/drawable-hdpi/navigate.png new file mode 100644 index 000000000..5bf5640aa Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/navigate.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause1.png b/TMessagesProj/src/main/res/drawable-hdpi/pause1.png deleted file mode 100755 index 30e945f31..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/pause1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause1_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/pause1_pressed.png deleted file mode 100755 index 0053b7b03..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/pause1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause2.png b/TMessagesProj/src/main/res/drawable-hdpi/pause2.png deleted file mode 100755 index f96f878e9..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/pause2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause2_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/pause2_pressed.png deleted file mode 100755 index c3bca6315..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/pause2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause_b.png b/TMessagesProj/src/main/res/drawable-hdpi/pause_b.png new file mode 100755 index 000000000..a9ac7a794 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/pause_b.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause_b_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/pause_b_pressed.png new file mode 100755 index 000000000..9f4b6e1db Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/pause_b_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause_g.png b/TMessagesProj/src/main/res/drawable-hdpi/pause_g.png new file mode 100755 index 000000000..b8bd88ef3 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/pause_g.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause_g_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/pause_g_pressed.png new file mode 100755 index 000000000..19c76b441 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/pause_g_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause_w.png b/TMessagesProj/src/main/res/drawable-hdpi/pause_w.png new file mode 100755 index 000000000..b3ba2dce6 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/pause_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause_w2.png b/TMessagesProj/src/main/res/drawable-hdpi/pause_w2.png new file mode 100755 index 000000000..c673dbc14 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/pause_w2.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause_w2_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/pause_w2_pressed.png new file mode 100755 index 000000000..7fdae4f8a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/pause_w2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause_w_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/pause_w_pressed.png new file mode 100755 index 000000000..ca28d541d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/pause_w_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/photo_crop.png b/TMessagesProj/src/main/res/drawable-hdpi/photo_crop.png old mode 100644 new mode 100755 index facaf39e7..aa78fee8b Binary files a/TMessagesProj/src/main/res/drawable-hdpi/photo_crop.png and b/TMessagesProj/src/main/res/drawable-hdpi/photo_crop.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/photo_edit.png b/TMessagesProj/src/main/res/drawable-hdpi/photo_edit.png deleted file mode 100644 index 8768155c2..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/photo_edit.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/photo_text.png b/TMessagesProj/src/main/res/drawable-hdpi/photo_text.png new file mode 100755 index 000000000..1009bbd79 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/photo_text.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/photo_text2.png b/TMessagesProj/src/main/res/drawable-hdpi/photo_text2.png new file mode 100755 index 000000000..9e0fc848b Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/photo_text2.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/photo_tools.png b/TMessagesProj/src/main/res/drawable-hdpi/photo_tools.png new file mode 100755 index 000000000..195c6626f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/photo_tools.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pin.png b/TMessagesProj/src/main/res/drawable-hdpi/pin.png new file mode 100644 index 000000000..386acb228 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/pin.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/place_x.png b/TMessagesProj/src/main/res/drawable-hdpi/place_x.png new file mode 100644 index 000000000..c4ee065d3 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/place_x.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/play1.png b/TMessagesProj/src/main/res/drawable-hdpi/play1.png deleted file mode 100755 index 8bb953bcf..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/play1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/play1_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/play1_pressed.png deleted file mode 100755 index d5d220250..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/play1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/play2.png b/TMessagesProj/src/main/res/drawable-hdpi/play2.png deleted file mode 100755 index 775a87a6a..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/play2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/play2_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/play2_pressed.png deleted file mode 100755 index 08e691986..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/play2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/play_w.png b/TMessagesProj/src/main/res/drawable-hdpi/play_w.png new file mode 100755 index 000000000..3b9bac668 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/play_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/play_w2.png b/TMessagesProj/src/main/res/drawable-hdpi/play_w2.png new file mode 100755 index 000000000..330da7e1e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/play_w2.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/play_w2_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/play_w2_pressed.png new file mode 100755 index 000000000..7b7018e2a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/play_w2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/play_w_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/play_w_pressed.png new file mode 100755 index 000000000..7261585f5 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/play_w_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/round_grey.png b/TMessagesProj/src/main/res/drawable-hdpi/round_grey.png new file mode 100755 index 000000000..6f9f3f215 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/round_grey.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/tune.png b/TMessagesProj/src/main/res/drawable-hdpi/tune.png deleted file mode 100755 index ab404e6a5..000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/tune.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/addmember.png b/TMessagesProj/src/main/res/drawable-mdpi/addmember.png new file mode 100755 index 000000000..439b82d41 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/addmember.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/arrow_down_w.png b/TMessagesProj/src/main/res/drawable-mdpi/arrow_down_w.png new file mode 100755 index 000000000..02ad9766d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/arrow_down_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/audiocancel1.png b/TMessagesProj/src/main/res/drawable-mdpi/audiocancel1.png deleted file mode 100755 index 3ec699799..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/audiocancel1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/audiocancel1_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/audiocancel1_pressed.png deleted file mode 100755 index 81b12fca1..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/audiocancel1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/audiocancel2.png b/TMessagesProj/src/main/res/drawable-mdpi/audiocancel2.png deleted file mode 100755 index f626dd957..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/audiocancel2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/audiocancel2_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/audiocancel2_pressed.png deleted file mode 100755 index 2129a26cf..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/audiocancel2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/audioload1.png b/TMessagesProj/src/main/res/drawable-mdpi/audioload1.png deleted file mode 100755 index e06d062f2..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/audioload1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/audioload1_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/audioload1_pressed.png deleted file mode 100755 index 96774c41a..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/audioload1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/audioload2.png b/TMessagesProj/src/main/res/drawable-mdpi/audioload2.png deleted file mode 100755 index cad0562f4..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/audioload2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/audioload2_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/audioload2_pressed.png deleted file mode 100755 index b1c3c6c0c..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/audioload2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/bg_emoji_bs.9.png b/TMessagesProj/src/main/res/drawable-mdpi/bg_emoji_bs.9.png deleted file mode 100644 index 1d6f546b1..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/bg_emoji_bs.9.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/cancel_b.png b/TMessagesProj/src/main/res/drawable-mdpi/cancel_b.png new file mode 100755 index 000000000..b0dd70ea0 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/cancel_b.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/cancel_b_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/cancel_b_pressed.png new file mode 100755 index 000000000..412153274 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/cancel_b_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/cancel_g.png b/TMessagesProj/src/main/res/drawable-mdpi/cancel_g.png new file mode 100755 index 000000000..43b0a71e4 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/cancel_g.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/cancel_g_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/cancel_g_pressed.png new file mode 100755 index 000000000..4942c02cb Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/cancel_g_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/download_b.png b/TMessagesProj/src/main/res/drawable-mdpi/download_b.png new file mode 100755 index 000000000..7c3fb80ce Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/download_b.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/download_b_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/download_b_pressed.png new file mode 100755 index 000000000..7e5385dd7 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/download_b_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/download_g.png b/TMessagesProj/src/main/res/drawable-mdpi/download_g.png new file mode 100755 index 000000000..2b6c9b925 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/download_g.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/download_g_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/download_g_pressed.png new file mode 100755 index 000000000..a1270b164 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/download_g_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/foursquare.png b/TMessagesProj/src/main/res/drawable-mdpi/foursquare.png new file mode 100644 index 000000000..e13d17641 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/foursquare.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/header_shadow_reverse.png b/TMessagesProj/src/main/res/drawable-mdpi/header_shadow_reverse.png new file mode 100644 index 000000000..cab866e17 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/header_shadow_reverse.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_ab_location.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_ab_location.png deleted file mode 100755 index 19fc6d1c3..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_ab_location.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_keyboard_w.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_keyboard_w.png new file mode 100755 index 000000000..1f4614aab Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/ic_keyboard_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smile_w.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smile_w.png new file mode 100755 index 000000000..a110abc10 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smile_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_backspace.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_backspace.png old mode 100644 new mode 100755 index 76076a60a..d63ba596a Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_backspace.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_backspace.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_backspace_active.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_backspace_active.png old mode 100644 new mode 100755 index 23bbc4b88..45140c69f Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_backspace_active.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_backspace_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_bell.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_bell.png old mode 100644 new mode 100755 index 1242d7dcd..8807b989f Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_bell.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_bell.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_bell_active.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_bell_active.png old mode 100644 new mode 100755 index 647f84536..6536bc7b3 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_bell_active.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_bell_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_car.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_car.png old mode 100644 new mode 100755 index 4c9d24fcd..e52411af8 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_car.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_car.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_car_active.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_car_active.png old mode 100644 new mode 100755 index f52038ef7..360b8e85d Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_car_active.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_car_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_flower.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_flower.png old mode 100644 new mode 100755 index aece41763..867f5b3f0 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_flower.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_flower.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_flower_active.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_flower_active.png old mode 100644 new mode 100755 index 26026ef5d..53c890bc8 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_flower_active.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_flower_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_grid.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_grid.png old mode 100644 new mode 100755 index 3d8934c0e..844206619 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_grid.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_grid.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_grid_active.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_grid_active.png old mode 100644 new mode 100755 index 6bea9c24d..926576661 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_grid_active.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_grid_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_recent.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_recent.png old mode 100644 new mode 100755 index 41d6f8a29..d306da4f3 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_recent.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_recent.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_recent_active.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_recent_active.png old mode 100644 new mode 100755 index 0f8caad5c..c208b675e Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_recent_active.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_recent_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_smile.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_smile.png old mode 100644 new mode 100755 index 9b7c59f8f..eb7c383dc Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_smile.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_smile.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_smile_active.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_smile_active.png old mode 100644 new mode 100755 index f62176957..1de2e16a7 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_smile_active.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_smile_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_sticker.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_sticker.png new file mode 100755 index 000000000..b76d05515 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_sticker.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_sticker_active.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_sticker_active.png new file mode 100755 index 000000000..056a2d0d5 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/ic_smiles_sticker_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/location_b.9.png b/TMessagesProj/src/main/res/drawable-mdpi/location_b.9.png new file mode 100644 index 000000000..3f0abf8b4 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/location_b.9.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/location_g.9.png b/TMessagesProj/src/main/res/drawable-mdpi/location_g.9.png new file mode 100644 index 000000000..d77aa911d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/location_g.9.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/myloc_on.png b/TMessagesProj/src/main/res/drawable-mdpi/myloc_on.png new file mode 100644 index 000000000..a86dd1570 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/myloc_on.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/navigate.png b/TMessagesProj/src/main/res/drawable-mdpi/navigate.png new file mode 100644 index 000000000..872d18fa7 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/navigate.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause1.png b/TMessagesProj/src/main/res/drawable-mdpi/pause1.png deleted file mode 100755 index 35570d84a..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/pause1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause1_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/pause1_pressed.png deleted file mode 100755 index 557cac120..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/pause1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause2.png b/TMessagesProj/src/main/res/drawable-mdpi/pause2.png deleted file mode 100755 index 50f4366db..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/pause2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause2_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/pause2_pressed.png deleted file mode 100755 index 50305fead..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/pause2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause_b.png b/TMessagesProj/src/main/res/drawable-mdpi/pause_b.png new file mode 100755 index 000000000..4d74e901d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/pause_b.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause_b_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/pause_b_pressed.png new file mode 100755 index 000000000..e300fbbca Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/pause_b_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause_g.png b/TMessagesProj/src/main/res/drawable-mdpi/pause_g.png new file mode 100755 index 000000000..a95855259 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/pause_g.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause_g_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/pause_g_pressed.png new file mode 100755 index 000000000..82e15e54d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/pause_g_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause_w.png b/TMessagesProj/src/main/res/drawable-mdpi/pause_w.png new file mode 100755 index 000000000..a6416a69e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/pause_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause_w2.png b/TMessagesProj/src/main/res/drawable-mdpi/pause_w2.png new file mode 100755 index 000000000..a5d419e71 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/pause_w2.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause_w2_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/pause_w2_pressed.png new file mode 100755 index 000000000..03dad7468 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/pause_w2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause_w_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/pause_w_pressed.png new file mode 100755 index 000000000..b12e36df9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/pause_w_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/photo_crop.png b/TMessagesProj/src/main/res/drawable-mdpi/photo_crop.png old mode 100644 new mode 100755 index 103f1c262..9133959b4 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/photo_crop.png and b/TMessagesProj/src/main/res/drawable-mdpi/photo_crop.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/photo_edit.png b/TMessagesProj/src/main/res/drawable-mdpi/photo_edit.png deleted file mode 100644 index 437baede1..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/photo_edit.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/photo_text.png b/TMessagesProj/src/main/res/drawable-mdpi/photo_text.png new file mode 100755 index 000000000..b1301d532 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/photo_text.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/photo_text2.png b/TMessagesProj/src/main/res/drawable-mdpi/photo_text2.png new file mode 100755 index 000000000..bbc1835bd Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/photo_text2.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/photo_tools.png b/TMessagesProj/src/main/res/drawable-mdpi/photo_tools.png new file mode 100755 index 000000000..86296a075 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/photo_tools.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pin.png b/TMessagesProj/src/main/res/drawable-mdpi/pin.png new file mode 100644 index 000000000..ce126ca8c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/pin.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/place_x.png b/TMessagesProj/src/main/res/drawable-mdpi/place_x.png new file mode 100644 index 000000000..3e4869bf6 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/place_x.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/play1.png b/TMessagesProj/src/main/res/drawable-mdpi/play1.png deleted file mode 100755 index 25cee312d..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/play1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/play1_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/play1_pressed.png deleted file mode 100755 index 124eb2479..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/play1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/play2.png b/TMessagesProj/src/main/res/drawable-mdpi/play2.png deleted file mode 100755 index 9d5a7c907..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/play2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/play2_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/play2_pressed.png deleted file mode 100755 index 52ce63376..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/play2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/play_w.png b/TMessagesProj/src/main/res/drawable-mdpi/play_w.png new file mode 100755 index 000000000..ca0df62f6 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/play_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/play_w2.png b/TMessagesProj/src/main/res/drawable-mdpi/play_w2.png new file mode 100755 index 000000000..5fba3f1b9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/play_w2.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/play_w2_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/play_w2_pressed.png new file mode 100755 index 000000000..7b9fc05b3 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/play_w2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/play_w_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/play_w_pressed.png new file mode 100755 index 000000000..b8b601b8a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/play_w_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/round_grey.png b/TMessagesProj/src/main/res/drawable-mdpi/round_grey.png new file mode 100755 index 000000000..7753080de Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/round_grey.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/tune.png b/TMessagesProj/src/main/res/drawable-mdpi/tune.png deleted file mode 100755 index 633d4b02d..000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/tune.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-v21/bar_selector_grey.xml b/TMessagesProj/src/main/res/drawable-v21/bar_selector_grey.xml new file mode 100644 index 000000000..fc4dddc7f --- /dev/null +++ b/TMessagesProj/src/main/res/drawable-v21/bar_selector_grey.xml @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/addmember.png b/TMessagesProj/src/main/res/drawable-xhdpi/addmember.png new file mode 100755 index 000000000..8440ca18f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/addmember.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/arrow_down_w.png b/TMessagesProj/src/main/res/drawable-xhdpi/arrow_down_w.png new file mode 100755 index 000000000..a94e1d57b Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/arrow_down_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel1.png b/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel1.png deleted file mode 100755 index 9a589b40c..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel1_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel1_pressed.png deleted file mode 100755 index 405882168..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel2.png b/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel2.png deleted file mode 100755 index 8af795283..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel2_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel2_pressed.png deleted file mode 100755 index b5ca6b6e4..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/audioload1.png b/TMessagesProj/src/main/res/drawable-xhdpi/audioload1.png deleted file mode 100755 index 48db9f061..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/audioload1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/audioload1_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/audioload1_pressed.png deleted file mode 100755 index 48db9f061..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/audioload1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/audioload2.png b/TMessagesProj/src/main/res/drawable-xhdpi/audioload2.png deleted file mode 100755 index 02dbe77b2..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/audioload2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/audioload2_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/audioload2_pressed.png deleted file mode 100755 index 434cc9d22..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/audioload2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/bg_emoji_bs.9.png b/TMessagesProj/src/main/res/drawable-xhdpi/bg_emoji_bs.9.png deleted file mode 100644 index 54c8f9ac8..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/bg_emoji_bs.9.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/cancel_b.png b/TMessagesProj/src/main/res/drawable-xhdpi/cancel_b.png new file mode 100755 index 000000000..36c6390ed Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/cancel_b.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/cancel_b_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/cancel_b_pressed.png new file mode 100755 index 000000000..79f951727 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/cancel_b_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/cancel_g.png b/TMessagesProj/src/main/res/drawable-xhdpi/cancel_g.png new file mode 100755 index 000000000..a77e504f2 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/cancel_g.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/cancel_g_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/cancel_g_pressed.png new file mode 100755 index 000000000..dea2364da Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/cancel_g_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/download_b.png b/TMessagesProj/src/main/res/drawable-xhdpi/download_b.png new file mode 100755 index 000000000..5d114b083 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/download_b.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/download_b_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/download_b_pressed.png new file mode 100755 index 000000000..671f591b2 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/download_b_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/download_g.png b/TMessagesProj/src/main/res/drawable-xhdpi/download_g.png new file mode 100755 index 000000000..1eaf6cfcd Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/download_g.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/download_g_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/download_g_pressed.png new file mode 100755 index 000000000..4d785fbbe Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/download_g_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/foursquare.png b/TMessagesProj/src/main/res/drawable-xhdpi/foursquare.png new file mode 100644 index 000000000..2b5bb36dc Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/foursquare.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/header_shadow_reverse.png b/TMessagesProj/src/main/res/drawable-xhdpi/header_shadow_reverse.png new file mode 100644 index 000000000..b8ab34806 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/header_shadow_reverse.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_ab_location.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_ab_location.png deleted file mode 100755 index ebc61a535..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_ab_location.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_keyboard_w.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_keyboard_w.png new file mode 100755 index 000000000..4866b74e4 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_keyboard_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smile_w.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smile_w.png new file mode 100755 index 000000000..f03eb4053 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smile_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_backspace.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_backspace.png old mode 100644 new mode 100755 index 561f125e3..3d4d64c15 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_backspace.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_backspace.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_backspace_active.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_backspace_active.png old mode 100644 new mode 100755 index 204a9ee4c..6f76811cb Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_backspace_active.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_backspace_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_bell.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_bell.png old mode 100644 new mode 100755 index d299db86b..faad89895 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_bell.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_bell.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_bell_active.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_bell_active.png old mode 100644 new mode 100755 index 075fc5fa9..ebdb0c9e2 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_bell_active.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_bell_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_car.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_car.png old mode 100644 new mode 100755 index 0c8519f11..5d312c39c Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_car.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_car.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_car_active.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_car_active.png old mode 100644 new mode 100755 index 3bae05c06..152f0c1bd Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_car_active.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_car_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_flower.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_flower.png old mode 100644 new mode 100755 index c5643d4fd..c9e2a36a2 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_flower.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_flower.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_flower_active.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_flower_active.png old mode 100644 new mode 100755 index fc5082c78..d95654808 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_flower_active.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_flower_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_grid.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_grid.png old mode 100644 new mode 100755 index 550d0016c..aae876d0b Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_grid.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_grid.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_grid_active.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_grid_active.png old mode 100644 new mode 100755 index 4d3758fc6..ea9338477 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_grid_active.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_grid_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_recent.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_recent.png old mode 100644 new mode 100755 index 69f4c19ac..aa1b12a7e Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_recent.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_recent.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_recent_active.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_recent_active.png old mode 100644 new mode 100755 index 9b270fc31..950176fdf Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_recent_active.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_recent_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_smile.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_smile.png old mode 100644 new mode 100755 index e367ee699..0f94a602e Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_smile.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_smile.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_smile_active.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_smile_active.png old mode 100644 new mode 100755 index cc7b338e3..6b883ef6f Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_smile_active.png and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_smile_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_sticker.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_sticker.png new file mode 100755 index 000000000..8f423142f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_sticker.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_sticker_active.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_sticker_active.png new file mode 100755 index 000000000..b2141d6cb Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_smiles_sticker_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/location_b.9.png b/TMessagesProj/src/main/res/drawable-xhdpi/location_b.9.png new file mode 100644 index 000000000..ade7b9ef9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/location_b.9.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/location_g.9.png b/TMessagesProj/src/main/res/drawable-xhdpi/location_g.9.png new file mode 100644 index 000000000..bbaeb93c4 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/location_g.9.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/myloc_on.png b/TMessagesProj/src/main/res/drawable-xhdpi/myloc_on.png new file mode 100644 index 000000000..0ec99ef2a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/myloc_on.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/navigate.png b/TMessagesProj/src/main/res/drawable-xhdpi/navigate.png new file mode 100644 index 000000000..6c7b7e134 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/navigate.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pause1.png b/TMessagesProj/src/main/res/drawable-xhdpi/pause1.png deleted file mode 100755 index 5a0abe48a..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/pause1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pause1_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/pause1_pressed.png deleted file mode 100755 index 799a2314b..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/pause1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pause2.png b/TMessagesProj/src/main/res/drawable-xhdpi/pause2.png deleted file mode 100755 index 973b1675b..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/pause2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pause2_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/pause2_pressed.png deleted file mode 100755 index 09e75bb8c..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/pause2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pause_b.png b/TMessagesProj/src/main/res/drawable-xhdpi/pause_b.png new file mode 100755 index 000000000..851500ecd Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/pause_b.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pause_b_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/pause_b_pressed.png new file mode 100755 index 000000000..ab048657c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/pause_b_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pause_g.png b/TMessagesProj/src/main/res/drawable-xhdpi/pause_g.png new file mode 100755 index 000000000..0708a5ede Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/pause_g.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pause_g_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/pause_g_pressed.png new file mode 100755 index 000000000..c6d72f73e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/pause_g_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pause_w.png b/TMessagesProj/src/main/res/drawable-xhdpi/pause_w.png new file mode 100755 index 000000000..9228d6e52 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/pause_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pause_w2.png b/TMessagesProj/src/main/res/drawable-xhdpi/pause_w2.png new file mode 100755 index 000000000..bcb65abfa Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/pause_w2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pause_w2_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/pause_w2_pressed.png new file mode 100755 index 000000000..7b7e39079 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/pause_w2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pause_w_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/pause_w_pressed.png new file mode 100755 index 000000000..214572b3e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/pause_w_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/photo_crop.png b/TMessagesProj/src/main/res/drawable-xhdpi/photo_crop.png old mode 100644 new mode 100755 index d69c3c12a..312b56546 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/photo_crop.png and b/TMessagesProj/src/main/res/drawable-xhdpi/photo_crop.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/photo_edit.png b/TMessagesProj/src/main/res/drawable-xhdpi/photo_edit.png deleted file mode 100644 index 33176600b..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/photo_edit.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/photo_text.png b/TMessagesProj/src/main/res/drawable-xhdpi/photo_text.png new file mode 100755 index 000000000..4faf7715a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/photo_text.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/photo_text2.png b/TMessagesProj/src/main/res/drawable-xhdpi/photo_text2.png new file mode 100755 index 000000000..244a4b355 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/photo_text2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/photo_tools.png b/TMessagesProj/src/main/res/drawable-xhdpi/photo_tools.png new file mode 100755 index 000000000..c4ff608ae Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/photo_tools.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pin.png b/TMessagesProj/src/main/res/drawable-xhdpi/pin.png new file mode 100644 index 000000000..1fa36f892 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/pin.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/place_x.png b/TMessagesProj/src/main/res/drawable-xhdpi/place_x.png new file mode 100644 index 000000000..3d95f25f9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/place_x.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/play1.png b/TMessagesProj/src/main/res/drawable-xhdpi/play1.png deleted file mode 100755 index 442474dac..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/play1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/play1_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/play1_pressed.png deleted file mode 100755 index 9391b98f7..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/play1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/play2.png b/TMessagesProj/src/main/res/drawable-xhdpi/play2.png deleted file mode 100755 index a2bf525bd..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/play2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/play2_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/play2_pressed.png deleted file mode 100755 index 164ff9e10..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/play2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/play_w.png b/TMessagesProj/src/main/res/drawable-xhdpi/play_w.png new file mode 100755 index 000000000..77d1a292d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/play_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/play_w2.png b/TMessagesProj/src/main/res/drawable-xhdpi/play_w2.png new file mode 100755 index 000000000..580da8336 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/play_w2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/play_w2_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/play_w2_pressed.png new file mode 100755 index 000000000..9bdc7f202 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/play_w2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/play_w_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/play_w_pressed.png new file mode 100755 index 000000000..11b12a54c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/play_w_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/round_grey.png b/TMessagesProj/src/main/res/drawable-xhdpi/round_grey.png new file mode 100755 index 000000000..de6e1f5cc Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/round_grey.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/tune.png b/TMessagesProj/src/main/res/drawable-xhdpi/tune.png deleted file mode 100755 index c6d6b9c7f..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/tune.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/addmember.png b/TMessagesProj/src/main/res/drawable-xxhdpi/addmember.png new file mode 100755 index 000000000..b4a262083 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/addmember.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/arrow_down_w.png b/TMessagesProj/src/main/res/drawable-xxhdpi/arrow_down_w.png new file mode 100755 index 000000000..e7710ef9a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/arrow_down_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel1.png b/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel1.png deleted file mode 100755 index c7f705a60..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel1_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel1_pressed.png deleted file mode 100755 index ba16d325d..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel2.png deleted file mode 100755 index ff6cb6814..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel2_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel2_pressed.png deleted file mode 100755 index cc9f56b11..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/audioload1.png b/TMessagesProj/src/main/res/drawable-xxhdpi/audioload1.png deleted file mode 100755 index 15b9c0217..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/audioload1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/audioload1_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/audioload1_pressed.png deleted file mode 100755 index 3465e6e61..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/audioload1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/audioload2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/audioload2.png deleted file mode 100755 index 4699e159d..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/audioload2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/audioload2_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/audioload2_pressed.png deleted file mode 100755 index c206870fd..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/audioload2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/cancel_b.png b/TMessagesProj/src/main/res/drawable-xxhdpi/cancel_b.png new file mode 100755 index 000000000..2fa1d0c12 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/cancel_b.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/cancel_b_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/cancel_b_pressed.png new file mode 100755 index 000000000..8f3b4c08d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/cancel_b_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/cancel_g.png b/TMessagesProj/src/main/res/drawable-xxhdpi/cancel_g.png new file mode 100755 index 000000000..34c23a830 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/cancel_g.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/cancel_g_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/cancel_g_pressed.png new file mode 100755 index 000000000..a8585f32d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/cancel_g_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/download_b.png b/TMessagesProj/src/main/res/drawable-xxhdpi/download_b.png new file mode 100755 index 000000000..0199ccc17 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/download_b.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/download_b_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/download_b_pressed.png new file mode 100755 index 000000000..ba3e4dab4 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/download_b_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/download_g.png b/TMessagesProj/src/main/res/drawable-xxhdpi/download_g.png new file mode 100755 index 000000000..edc1b88e9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/download_g.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/download_g_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/download_g_pressed.png new file mode 100755 index 000000000..4a258462a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/download_g_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/foursquare.png b/TMessagesProj/src/main/res/drawable-xxhdpi/foursquare.png new file mode 100644 index 000000000..0104f7dc9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/foursquare.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/header_shadow_reverse.png b/TMessagesProj/src/main/res/drawable-xxhdpi/header_shadow_reverse.png new file mode 100644 index 000000000..3d39b80a1 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/header_shadow_reverse.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_ab_location.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_ab_location.png deleted file mode 100755 index 814bc8bd7..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_ab_location.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_keyboard_w.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_keyboard_w.png new file mode 100755 index 000000000..f6bb99dc9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_keyboard_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smile_w.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smile_w.png new file mode 100755 index 000000000..fd3453ebd Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smile_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_backspace.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_backspace.png old mode 100644 new mode 100755 index 83d822615..8580a8b67 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_backspace.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_backspace.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_backspace_active.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_backspace_active.png old mode 100644 new mode 100755 index 6a1900234..eb046dea7 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_backspace_active.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_backspace_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_bell.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_bell.png old mode 100644 new mode 100755 index e155c18ef..bcd04f991 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_bell.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_bell.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_bell_active.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_bell_active.png old mode 100644 new mode 100755 index 34be89d5c..701047dcc Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_bell_active.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_bell_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_car.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_car.png old mode 100644 new mode 100755 index 9b5299f1e..430ef9348 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_car.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_car.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_car_active.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_car_active.png old mode 100644 new mode 100755 index 03b0b7658..8568ffb25 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_car_active.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_car_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_flower.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_flower.png old mode 100644 new mode 100755 index 0843eb941..63ba2bcee Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_flower.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_flower.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_flower_active.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_flower_active.png old mode 100644 new mode 100755 index ed10b1369..852144ab4 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_flower_active.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_flower_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_grid.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_grid.png old mode 100644 new mode 100755 index 73e324c41..c701d3124 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_grid.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_grid.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_grid_active.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_grid_active.png old mode 100644 new mode 100755 index de877ff80..dc60847bb Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_grid_active.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_grid_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_recent.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_recent.png old mode 100644 new mode 100755 index 9f3778260..d08b38cd7 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_recent.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_recent.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_recent_active.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_recent_active.png old mode 100644 new mode 100755 index 232d52544..34fe4ff4d Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_recent_active.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_recent_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_smile.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_smile.png old mode 100644 new mode 100755 index c473c122d..0e8515a12 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_smile.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_smile.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_smile_active.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_smile_active.png old mode 100644 new mode 100755 index 1981d0c77..36dff7616 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_smile_active.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_smile_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_sticker.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_sticker.png new file mode 100755 index 000000000..91e9a0746 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_sticker.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_sticker_active.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_sticker_active.png new file mode 100755 index 000000000..c0b2af66c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_smiles_sticker_active.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/location_b.9.png b/TMessagesProj/src/main/res/drawable-xxhdpi/location_b.9.png new file mode 100644 index 000000000..01ea9f4dd Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/location_b.9.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/location_g.9.png b/TMessagesProj/src/main/res/drawable-xxhdpi/location_g.9.png new file mode 100644 index 000000000..7343d9636 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/location_g.9.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/myloc_on.png b/TMessagesProj/src/main/res/drawable-xxhdpi/myloc_on.png new file mode 100644 index 000000000..1b40c0f1b Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/myloc_on.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/navigate.png b/TMessagesProj/src/main/res/drawable-xxhdpi/navigate.png new file mode 100644 index 000000000..ffe55f7fe Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/navigate.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause1.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause1.png deleted file mode 100755 index 9293c448b..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/pause1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause1_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause1_pressed.png deleted file mode 100755 index e86f91f93..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/pause1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause2.png deleted file mode 100755 index d18cf420d..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/pause2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause2_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause2_pressed.png deleted file mode 100755 index f7259a314..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/pause2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause_b.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_b.png new file mode 100755 index 000000000..fa34156e3 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_b.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause_b_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_b_pressed.png new file mode 100755 index 000000000..14417454f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_b_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause_g.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_g.png new file mode 100755 index 000000000..58230127c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_g.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause_g_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_g_pressed.png new file mode 100755 index 000000000..6afeea45e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_g_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause_w.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_w.png new file mode 100755 index 000000000..31bdd7380 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause_w2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_w2.png new file mode 100755 index 000000000..d4be9ec78 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_w2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause_w2_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_w2_pressed.png new file mode 100755 index 000000000..94ce677c2 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_w2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause_w_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_w_pressed.png new file mode 100755 index 000000000..0f81862fe Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/pause_w_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/photo_crop.png b/TMessagesProj/src/main/res/drawable-xxhdpi/photo_crop.png old mode 100644 new mode 100755 index 460fdb38d..1276ad189 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/photo_crop.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/photo_crop.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/photo_edit.png b/TMessagesProj/src/main/res/drawable-xxhdpi/photo_edit.png deleted file mode 100644 index 6df74a5b3..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/photo_edit.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/photo_text.png b/TMessagesProj/src/main/res/drawable-xxhdpi/photo_text.png new file mode 100755 index 000000000..41c6315d7 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/photo_text.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/photo_text2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/photo_text2.png new file mode 100755 index 000000000..461b2a3e2 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/photo_text2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/photo_tools.png b/TMessagesProj/src/main/res/drawable-xxhdpi/photo_tools.png new file mode 100755 index 000000000..ab1bb0e95 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/photo_tools.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pin.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pin.png new file mode 100644 index 000000000..8580d9a2f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/pin.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/place_x.png b/TMessagesProj/src/main/res/drawable-xxhdpi/place_x.png new file mode 100644 index 000000000..aa7aebba0 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/place_x.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/play1.png b/TMessagesProj/src/main/res/drawable-xxhdpi/play1.png deleted file mode 100755 index 1c0924fdf..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/play1.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/play1_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/play1_pressed.png deleted file mode 100755 index 51642f673..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/play1_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/play2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/play2.png deleted file mode 100755 index d34c30371..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/play2.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/play2_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/play2_pressed.png deleted file mode 100755 index cff0cf086..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/play2_pressed.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/play_w.png b/TMessagesProj/src/main/res/drawable-xxhdpi/play_w.png new file mode 100755 index 000000000..be2cb867f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/play_w.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/play_w2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/play_w2.png new file mode 100755 index 000000000..263f27db6 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/play_w2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/play_w2_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/play_w2_pressed.png new file mode 100755 index 000000000..96e0e1276 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/play_w2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/play_w_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/play_w_pressed.png new file mode 100755 index 000000000..48aa6b658 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/play_w_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/round_grey.png b/TMessagesProj/src/main/res/drawable-xxhdpi/round_grey.png new file mode 100755 index 000000000..7b037c623 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/round_grey.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/tune.png b/TMessagesProj/src/main/res/drawable-xxhdpi/tune.png deleted file mode 100755 index eda3e5a32..000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/tune.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable/background_tab.xml b/TMessagesProj/src/main/res/drawable/background_tab.xml deleted file mode 100755 index 885cf036a..000000000 --- a/TMessagesProj/src/main/res/drawable/background_tab.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/drawable/bar_selector_grey.xml b/TMessagesProj/src/main/res/drawable/bar_selector_grey.xml new file mode 100644 index 000000000..f311824b8 --- /dev/null +++ b/TMessagesProj/src/main/res/drawable/bar_selector_grey.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/drawable/ic_emoji_backspace.xml b/TMessagesProj/src/main/res/drawable/ic_emoji_backspace.xml index 0346dc27c..41ea5b772 100644 --- a/TMessagesProj/src/main/res/drawable/ic_emoji_backspace.xml +++ b/TMessagesProj/src/main/res/drawable/ic_emoji_backspace.xml @@ -1,10 +1,11 @@ - - - - + + + + + + diff --git a/TMessagesProj/src/main/res/drawable/ic_emoji_bell.xml b/TMessagesProj/src/main/res/drawable/ic_emoji_bell.xml index 5c3d57160..17b59fd60 100644 --- a/TMessagesProj/src/main/res/drawable/ic_emoji_bell.xml +++ b/TMessagesProj/src/main/res/drawable/ic_emoji_bell.xml @@ -2,15 +2,12 @@ + android:exitFadeDuration="300"> + android:drawable="@drawable/ic_smiles_bell_active"> + android:drawable="@drawable/ic_smiles_bell"> diff --git a/TMessagesProj/src/main/res/drawable/ic_emoji_sticker.xml b/TMessagesProj/src/main/res/drawable/ic_emoji_sticker.xml new file mode 100644 index 000000000..2cb1ecbef --- /dev/null +++ b/TMessagesProj/src/main/res/drawable/ic_emoji_sticker.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/TMessagesProj/src/main/res/layout-ar/location_view_layout.xml b/TMessagesProj/src/main/res/layout-ar/location_view_layout.xml deleted file mode 100644 index a39c6a5e5..000000000 --- a/TMessagesProj/src/main/res/layout-ar/location_view_layout.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/layout/document_select_layout.xml b/TMessagesProj/src/main/res/layout/document_select_layout.xml index 9576e5914..9c9329f61 100644 --- a/TMessagesProj/src/main/res/layout/document_select_layout.xml +++ b/TMessagesProj/src/main/res/layout/document_select_layout.xml @@ -4,8 +4,8 @@ android:layout_height="match_parent"> - + android:layout_width="match_parent" + android:layout_height="match_parent"> - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/layout/location_view_layout.xml b/TMessagesProj/src/main/res/layout/location_view_layout.xml deleted file mode 100644 index d89e26ebd..000000000 --- a/TMessagesProj/src/main/res/layout/location_view_layout.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/layout/messages_list.xml b/TMessagesProj/src/main/res/layout/messages_list.xml deleted file mode 100644 index 6f6381a86..000000000 --- a/TMessagesProj/src/main/res/layout/messages_list.xml +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TMessagesProj/src/main/res/layout/popup_count_layout.xml b/TMessagesProj/src/main/res/layout/popup_count_layout.xml index 1593006af..6011dc4cb 100644 --- a/TMessagesProj/src/main/res/layout/popup_count_layout.xml +++ b/TMessagesProj/src/main/res/layout/popup_count_layout.xml @@ -5,7 +5,7 @@ - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/layout/settings_wallpapers_layout.xml b/TMessagesProj/src/main/res/layout/settings_wallpapers_layout.xml deleted file mode 100644 index a49b123c4..000000000 --- a/TMessagesProj/src/main/res/layout/settings_wallpapers_layout.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/layout/settings_wallpapers_my_row.xml b/TMessagesProj/src/main/res/layout/settings_wallpapers_my_row.xml deleted file mode 100644 index d0683c0b1..000000000 --- a/TMessagesProj/src/main/res/layout/settings_wallpapers_my_row.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/layout/settings_wallpapers_other_row.xml b/TMessagesProj/src/main/res/layout/settings_wallpapers_other_row.xml deleted file mode 100644 index 882ae732f..000000000 --- a/TMessagesProj/src/main/res/layout/settings_wallpapers_other_row.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/raw/sound_in.wav b/TMessagesProj/src/main/res/raw/sound_in.wav index 262201668..dd5921544 100644 Binary files a/TMessagesProj/src/main/res/raw/sound_in.wav and b/TMessagesProj/src/main/res/raw/sound_in.wav differ diff --git a/TMessagesProj/src/main/res/values-ar/strings.xml b/TMessagesProj/src/main/res/values-ar/strings.xml index 743678431..b3877943f 100644 --- a/TMessagesProj/src/main/res/values-ar/strings.xml +++ b/TMessagesProj/src/main/res/values-ar/strings.xml @@ -15,7 +15,7 @@ رمز التفعيل تم إرسال رسالة قصيرة تحتوي على رمز التفعيل الخاص بك - %1$d:%2$02d سنتصل بك خلال + سنتصل بك خلال %1$d:%2$02d جاري الاتصال بك ... رمز التفعيل الرقم خاطئ؟ @@ -89,6 +89,10 @@ %1$s يقوم بإرسال صورة... %1$s يقوم بإرسال مقطع مرئي... %1$s يقوم بإرسال ملف... + جاري تسجيل المقطع الصوتي... + جاري إرسال الصورة... + جاري إرسال المقطع المرئي... + جاري إرسال الملف... هل يوجد لديك سؤال\nحول تيليجرام؟ التقط صورة صورة @@ -122,6 +126,8 @@ عداد التدمير الذاتي إشعارات الخدمة جاري جلب معلومات الرابط... + فتح في المتصفح + انسخ الرابط %1$s قام بتعيين عداد التدمير الذاتي إلى to %2$s لقد قمت بتعيين التدمير الذاتي إلى %1$s @@ -157,6 +163,7 @@ %1$s قام بالتسجيل في تيليجرام! %1$s,\nتم تسجيل الدخول لحسابك من جهاز جديد يوم %2$s\n\nالجهاز: %3$s\nالموقع: %4$s\n\nإذا لم يكن أنت من سجل الدخول، يمكنك الذهاب للإعدادات ثم تسجيل الخروج من كافة الأجهزة الأخرى. كما يمكنك تفعيل التحقق بخطوتين إذا رغبت بذلك عن طريق إعدادات الخصوصية.\n\nشكرًا,\nفريق عمل تيليجرام %1$s قام بتغيير صورته الشخصية + %1$s قام بالدخول للمجموعة %2$s باستخدام رابط الدعوة الرد الرد على %1$s الرد على %1$s @@ -182,6 +189,19 @@ أدخل اسم للمجموعة اسم المجموعة %1$d/%2$d عضو + هل ترغب في الدخول للمجموعة \'%1$s\'؟ + المعذرة، هذه المجموعة ممتلئة. + المعذرة، هذه المجموعة غير موجودة. + تم نسخ الرابط إلى الحافظة + قم بدعوة للمجموعة باستخدام رابط + رابط دعوة + هل أنت متأكد من رغبتك في إلغاء رابط الدعوة؟ إذا قمت بذلك، لن يستطيع أحد من استخدامه للدخول للمجموعة. + رابط الدعوة السابق لا يعمل الآن. سيتم إنشاء رابط جديد. + إلغاء الرابط + إلغاء الرابط + نسخ الرابط + شارك الرابط + أي شخص يمتلك تيليجرام على جهازه سيسطيع الدخول لمجموعتك باستخدام الرابط التالي. عدد الوسائط المشتركة الإعدادات @@ -223,6 +243,21 @@ %1$s متاح. لا يوجد حدث خطأ. + + ملصقات + العقول العظيمة + الرسامون مرحب بهم ليصنعوا حزم ملصقات عن طريق مراسلة @stickers .\n\nيمكن إضافة هذه الحزم بالضغط مرتين على الملصق واختيار \"معلومات\" — \"إضافة الملصقات\". + إضافة ملصق + إضافة إلى الملصقات + لا يوجد ملصقات + تم حذف الملصقات + تم إضافة ملصقات جديدة + إخفاء + إظهار + مشاركة + نسخ الرابط + حذف + لا يوجد ملصقات بعد تم تعيين كافة الإشعارات افتراضيا حجم نص الرسائل @@ -311,6 +346,13 @@ إيقاف الأصوات داخل المحادثات افتراضي + إشعارات ذكية + تعطيل + أعلى صوت %1$s خلال %2$s + أعلى صوت + الأوقات + خلال + دقائق الأجهزة المسجّل دخول منها الجهاز الحالي @@ -350,15 +392,20 @@ هجين متر يبعد كيلومتر يبعد - أرسل موقعك - مشاركة الموقع + أرسل مكانك الحالي + أرسل المكان المختار + المكان + دقيق لدرجة %1$s + أو اختر مكان عرض كافة الوسائط حفظ في الجهاز %1$d من %2$d الألبوم جميع الصور + كافة المقاطع المرئية لا توجد صور حتى الآن + لا يوجد مقاطع مرئية بعد فضلًا، قم بتنزيل الوسائط أولًا لا توجد صور حديثة لا يوجد صور متحركة حديثة @@ -388,6 +435,11 @@ تجاهل التغييرات؟ هل ترغب في مسح سجل البحث؟ مسح + صور + مقطع مرئي + أضف تعليق... + تعليق الصورة + تعليق المقطع المرئي التحقق بخطوتين تعيين كلمة مرور إضافية @@ -482,6 +534,7 @@ تم فتح إلغاء + إغلاق إضافة تعديل إرسال @@ -497,6 +550,8 @@ موافق قطع + لقد قمت بالدخول للمجموعة باستخدام رابط الدعوة + un1 قام بالدخول للمجموعة باستخدام رابط الدعوة un1 أزال un2 غادر المجموعة العضو un1 un1 قام بإضافة un2 @@ -514,6 +569,7 @@ un1 قام بإخراجك un1 قام بإضافتك un1 عاد إلى المجموعة + لقد عدت إلى المجموعة نسخة تيليجرام الموجودة لديك لا تدعم هذه الرسالة. الرجاء التحديث لأحدث نسخة: http://telegram.org/update صورة مقطع مرئي @@ -664,6 +720,24 @@ %1$d مستخدمون %1$d مستخدم %1$d مستخدم + %1$d مرات + %1$d مرة + %1$d مرة + %1$d مرة + %1$d مرة + %1$d مرة + %1$d أمتار + %1$d متر + %1$d متر + %1$d متر + %1$d متر + %1$d متر + %1$d ملصقات + %1$d ملصق + %1$d ملصق + %1$d ملصقات + %1$d ملصق + %1$d ملصق %1$d رسالة معاد توجيهها الرسالة المعاد توجيهها @@ -730,4 +804,7 @@ HH:mm h:mm a %1$s الساعة %2$s + + تيليجرام نسخة الـ Android تم تحديثه. الجديد في نسخة ٢.٩:\n\n- تنصيب ومشاركة الملصقات كهذه: https://telegram.me/addstickers/Animals\n- إذا كنت رسام، قم بصنع حزمة ملصقات عن طريق مراسلة @stickers .\n\n - استخدم تيليجرام مع أندرويد أوتو. + 536 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-de/strings.xml b/TMessagesProj/src/main/res/values-de/strings.xml index fd4c0035a..a044f7818 100644 --- a/TMessagesProj/src/main/res/values-de/strings.xml +++ b/TMessagesProj/src/main/res/values-de/strings.xml @@ -40,7 +40,7 @@ Aktualisiere… Neuer Geheimer Chat Warte, bis %s online geht… - Geheimen Chat beendet + Geheimer Chat beendet Tausche Schlüssel aus… %s ist deinem geheimen Chat beigetreten. Du bist dem geheimen Chat beigetreten. @@ -89,6 +89,10 @@ %1$s schickt Bild... %1$s schickt Video... %1$s schickt Datei... + nimmt etwas auf... + schickt Bild... + schickt Video... + schickt Datei... Hast du eine Frage\nzu Telegram? Foto aufnehmen Galerie @@ -101,7 +105,7 @@ Keine aktuellen Nachricht Nachricht - Meine Telefonnummer teilen + Meine Nummer teilen Zu Kontakten hinzufügen %s hat dich zu einem\nEnde-zu-Ende verschlüsselten\nGeheimen Chat eingeladen. Du hast %s zu einem\nEnde-zu-Ende verschlüsselten\nGeheimen Chat eingeladen. @@ -122,6 +126,8 @@ Selbstzerstörungs-Timer setzen Servicemeldungen Lade Linkvorschau... + Im Browser öffnen + URL kopieren %1$s hat den Selbstzerstörungs-Timer auf %2$s gesetzt Du hast den Selbstzerstörungs-Timer auf %1$s gesetzt @@ -155,8 +161,9 @@ %1$s hat dich aus der Gruppe %2$s entfernt %1$s hat die Gruppe %2$s verlassen %1$s benutzt jetzt Telegram! - %1$s,\nWir haben eine Anmeldung von einem neuen Gerät am %2$s festgestellt.\n\nGerät: %3$s\nStandort: %4$s\n\nWenn du das nicht selbst gewesen bist, melde alle anderen Sitzungen in den Telegram Einstellungen unverzüglich ab.\n\nBeachte unsere zweistufige Bestätigung, welche du in den Telegram Einstellungen unter Privatsphäre und Sicherheit optional aktivieren kannst.\n\nDein Telegram Team + %1$s,\nWir haben eine Anmeldung von einem neuen Gerät am %2$s festgestellt.\n\nGerät: %3$s\nStandort: %4$s\n\nWenn du das nicht selbst gewesen bist, melde die entsprechende Sitzung in den Telegram Einstellungen unter Privatsphäre und Sicherheit - Sitzungen unverzüglich ab.\n\nKennst du schon unsere zweistufige Bestätigung? Diese kannst du in den Telegram Einstellungen unter Privatsphäre und Sicherheit optional aktivieren.\n\nDein Telegram Team %1$s hat das Profilbild geändert + %1$s ist per Einladungslink der Gruppe %2$s beigetreten Antworten %1$s antworten %1$s antworten @@ -182,6 +189,19 @@ Gruppenname Gruppenname %1$d/%2$d Mitglieder + Möchtest du der Gruppe \"%1$s\" beitreten? + Leider ist diese Gruppe schon voll. + Leider gibt es diese Gruppe nicht. + Link in die Zwischenablage kopiert + Per Link zur Gruppe einladen + Einladungslink + Bist du sicher, dass du diesen Link widerrufen willst? Dadurch kann ihn niemand mehr nutzen. + Der vorige Link ist nun inaktiv. Ein neuer Einladungslink wurde gerade erstellt. + Widerrufen + Link widerrufen + Link kopieren + Link teilen + Jeder, der Telegram installiert hat, kann anhand dieses Links in deine Gruppe. Geteilte Medien Einstellungen @@ -218,11 +238,26 @@ Ein Benutzername benötigt mindestens 5 Zeichen. Ein Benutzername darf maximal 32 Zeichen haben. Benutzernamen dürfen leider nicht mit einer Zahl anfangen. - Wähle einen für jeden sichtbaren Benutzernamen, wenn du von anderen bei ]]>Telegram]]> gefunden werden willst — ohne, dass sie deine Nummer kennen müssen.
]]>Erlaubt sind ]]>a-z]]>, ]]>0-9]]> und Unterstriche. Die Mindestlänge beträgt ]]>5]]> Zeichen.
+ Wähle einen öffentlichen Benutzernamen, wenn du von anderen bei ]]>Telegram]]> gefunden werden willst — ohne, dass sie deine Nummer kennen müssen.
]]>Erlaubt sind ]]>a-z]]>, ]]>0-9]]> und Unterstriche. Die Mindestlänge beträgt ]]>5]]> Zeichen.
Prüfe Benutzername... %1$s ist verfügbar. Keiner Es ist ein Fehler aufgetreten. + + Sticker + Große Denker + Künstler können eigene Sticker-Pakete über unseren Sticker Bot (@stickers) einstellen.\n\nNutzer fügen neue Sticker durch doppeltes Antippen (\"Doppelklick\") eines Stickers und dann \"Info\" — \"Sticker hinzufügen\" hinzu. + Sticker hinzufügen + Sticker hinzufügen + Sticker nicht gefunden + Sticker entfernt + Neue Sticker hinzugefügt + Verstecken + Zeigen + Teilen + Link kopieren + Entfernen + Noch keine Sticker Alle Einstellungen für Mitteilungen zurücksetzen Textgröße für Nachrichten @@ -309,11 +344,18 @@ Deaktiviert Deaktiviert Aus - In-Chat Töne + In-Chat-Töne Standard + Intelligente Benachrichtigungen + Deaktiviert + Höchstens %1$s innerhalb von %2$s + Höchstens + Mal + innerhalb von + Minuten Sitzungen - Aktuelle Sitzung + Aktuelles Gerät Keine anderen Geräte Du kannst dich von jedem Handy, Tablet und Computer bei Telegram mit derselben Telefonnummer anmelden. Alles wird immer sofort synchronisiert. Andere Geräte @@ -350,15 +392,20 @@ Hybrid m entfernt km entfernt - Standort senden - Teile Standort + Meinen Standort senden + Diesen Standort senden + Standort + Auf %1$s genau + Oder wähle einen Ort Zeige alle Medien In der Galerie speichern %1$d von %2$d Galerie Alle Fotos + Alle Videos Noch keine Fotos + Noch keine Videos Medien bitte zuerst herunterladen Suchverlauf Suchverlauf @@ -388,6 +435,11 @@ Änderungen verwerfen? Suchverlauf löschen? Löschen + Bilder + Video + Beschriftung... + Bildbeschriftung + Videobeschriftung Zweistufige Bestätigung Zusätzliches Kennwort festlegen @@ -482,6 +534,7 @@ Fertig Öffnen Abbrechen + Schließen Hinzufügen Bearbeiten Senden @@ -497,6 +550,8 @@ OK SCHNEIDEN + Du bist der Gruppe per Link beigetreten + un1 ist der Gruppe per Link beigetreten un1 hat un2 aus der Gruppe entfernt un1 hat die Gruppe verlassen un1 hat un2 hinzugefügt @@ -514,6 +569,7 @@ un1 hat dich aus der Gruppe entfernt un1 hat dich hinzugefügt un1 ist in die Gruppe zurückgekehrt + Du bist in die Gruppe zurückgekehrt Diese Nachricht wird von deiner Telegram-Version nicht unterstützt. Bitte aktualisiere die App um sie zu sehen: http://telegram.org/update Foto Video @@ -664,6 +720,24 @@ %1$d Nutzer %1$d Nutzer %1$d Nutzer + %1$d Mal + %1$d Mal + %1$d Mal + %1$d Mal + %1$d Mal + %1$d Mal + %1$d Meter + %1$d Meter + %1$d Meter + %1$d Meter + %1$d Meter + %1$d Meter + %1$d Sticker + %1$d Sticker + %1$d Sticker + %1$d Sticker + %1$d Sticker + %1$d Sticker %1$d angehängten Nachrichten Angehängte Nachricht @@ -730,4 +804,7 @@ HH:mm h:mm a %1$s um %2$s + + Telegram für Android wurde aktualisiert. Neu in Version 2.9:\n\n- Installiere und teile benutzerdefinierte Sticker-Pakete, wie z.B. dieses: https://telegram.me/addstickers/Animals\n- Künstler können eigene Sticker-Pakete über unseren Sticker Bot (@stickers) einstellen.\n\n- Benutze Telegram mit Android Auto (siehe android.com/auto) + 536 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-es/strings.xml b/TMessagesProj/src/main/res/values-es/strings.xml index b2b2f7b37..02eb45317 100644 --- a/TMessagesProj/src/main/res/values-es/strings.xml +++ b/TMessagesProj/src/main/res/values-es/strings.xml @@ -89,6 +89,10 @@ %1$s está enviando una foto... %1$s está enviando un vídeo... %1$s está enviando un archivo... + grabando audio... + enviando foto... + enviando vídeo... + enviando archivo... ¿Tienes preguntas\nsobre Telegram? Hacer foto Galería @@ -122,6 +126,8 @@ Establecer autodestrucción Servicio de notificaciones Obteniendo información... + Abrir en el navegador + Copiar URL %1$s activó la autodestrucción en %2$s Activaste la autodestrucción en %1$s @@ -157,6 +163,7 @@ ¡%1$s se unió a Telegram! %1$s,\nDetectamos un inicio de sesión en tu cuenta desde un nuevo dispositivo, el %2$s\n\nDispositivo: %3$s\nUbicación: %4$s\n\nSi no eras tú, puedes ir a Ajustes - Privacidad y seguridad - Cerrar todas las otras sesiones.\n\nSi crees que alguien ha iniciado la sesión sin tu consentimiento, puedes activar la verificación en dos pasos, en los ajustes de privacidad y seguridad.\n\nAtentamente,\nEl equipo de Telegram %1$s actualizó su foto de perfil + %1$s se unió al grupo %2$s con un enlace de invitación Responder Responder a %1$s Responder a %1$s @@ -182,8 +189,21 @@ Nombre del grupo Nombre del grupo %1$d/%2$d miembros + ¿Quieres unirte al grupo \'%1$s\'? + Lo sentimos. Este grupo está lleno. + Lo sentimos. Este grupo no existe. + Enlace copiado al portapapeles + Invitar al grupo con un enlace + Enlace de invitación + ¿Quieres anular este enlace? Una vez hecho, nadie podrá usarlo para unirse al grupo. + El enlace de invitación anterior está inactivo. Ha sido creado uno nuevo. + Anular + Anular enlace + Copiar enlace + Compartir enlace + Cualquiera que tenga Telegram instalado podrá unirse a tu grupo siguiendo este enlace. - Multimedia compartida + Todos los archivos Ajustes Añadir miembro Eliminar y dejar el grupo @@ -211,18 +231,33 @@ Información Teléfono - Apodo - Tu apodo - Lo siento, este apodo ya está ocupado. - Lo siento, este apodo es inválido. - Un apodo debe tener al menos 5 caracteres. - El apodo no debe exceder los 32 caracteres. - Lo siento, un apodo no puede comenzar con un número. - Puedes elegir un apodo en ]]>Telegram]]>. Si lo haces, otras personas te podrán encontrar por ese apodo y contactarte sin saber tu número de teléfono.
]]>Puedes usar ]]>a–z]]>, ]]>0–9]]> y guiones bajos. La longitud mínima es de ]]>5]]> caracteres.
- Verificando apodo... + Alias + Tu alias + Lo siento, este alias ya está ocupado. + Lo siento, este alias es inválido. + Un alias debe tener al menos 5 caracteres. + El alias no debe exceder los 32 caracteres. + Lo siento, un alias no puede comenzar con un número. + Puedes elegir un alias en ]]>Telegram]]>. Si lo haces, otras personas te podrán encontrar por ese alias y contactarte sin saber tu número de teléfono.
]]>Puedes usar ]]>a–z]]>, ]]>0–9]]> y guiones bajos. La longitud mínima es de ]]>5]]> caracteres.
+ Verificando alias... %1$s está disponible. Ninguno Ocurrió un error. + + Stickers + Grandes personajes + Los artistas pueden añadir sus propios packs de stickers usando el bot @stickers.\n\nLos usuarios pueden añadir stickers pulsando y eligiendo \"Añadir stickers\". + Añadir stickers + Añadir a stickers + Stickers no encontrados + Stickers eliminados + Nuevos stickers añadidos + Ocultar + Mostrar + Compartir + Copiar enlace + Quitar + Sin stickers aún Restablecer las notificaciones Tamaño del texto @@ -311,6 +346,13 @@ Apagado Sonidos en el chat Por defecto + Notificaciones inteligentes + Desactivadas + Sonar como máximo %1$s en %2$s + Sonar como máximo + veces + en + minutos Sesiones activas Sesión actual @@ -350,15 +392,20 @@ Híbrido m de distancia km de distancia - Enviar ubicación + Enviar tu ubicación actual + Enviar la ubicación seleccionada Ubicación + Exacto a %1$s + O ELIGE UN LUGAR Ir a Multimedia Guardar en galería %1$d de %2$d Galería Todas las fotos + Todos los vídeos Aún sin fotos + Sin vídeos aún Por favor, primero descarga la multimedia No hay fotos recientes No hay GIF recientes @@ -388,6 +435,11 @@ ¿Descartar cambios? ¿Quieres borrar el historial de búsqueda? Borrar + Fotos + Vídeo + Añadir un comentario... + Comentario de foto + Comentario de vídeo Verificación en dos pasos Poner contraseña adicional @@ -482,6 +534,7 @@ Hecho Abrir Cancelar + Cerrar Añadir Editar Enviar @@ -497,6 +550,8 @@ OK RECORTAR + Te uniste al grupo con un enlace de invitación + un1 se unió al grupo con un enlace de invitación un1 expulsó a un2 un1 dejó el grupo un1 añadió a un2 @@ -514,6 +569,7 @@ un1 te expulsó un1 te añadió un1 volvió al grupo + Volviste al grupo Este mensaje no lo admite tu versión de Telegram. Actualiza la app para verlo: http://telegram.org/update Foto Vídeo @@ -537,7 +593,7 @@ No tienes reproductor de vídeo. Por favor, instala uno para continuar. Por favor, envía un correo electrónico a sms@stel.com y cuéntanos tu problema. No tienes aplicaciones que puedan manejar el tipo de archivo \'%1$s\'. Por favor, instala una para continuar. - Este usuario no tiene Telegram aún. ¿Enviarle una invitación? + Este usuario aún no tiene Telegram. ¿Enviarle una invitación? ¿Quieres hacerlo? ¿Añadir a %1$s al grupo?\n\nNúmero de los últimos mensajes para reenviar: ¿Reenviar mensajes a %1$s? @@ -664,6 +720,24 @@ %1$d usuarios %1$d usuarios %1$d usuarios + %1$d veces + %1$d vez + %1$d veces + %1$d veces + %1$d veces + %1$d veces + %1$d metros + %1$d metro + %1$d metros + %1$d metros + %1$d metros + %1$d metros + %1$d stickers + %1$d sticker + %1$d stickers + %1$d stickers + %1$d stickers + %1$d stickers %1$d mensajes adjuntos Mensaje adjunto @@ -730,4 +804,7 @@ HH:mm h:mm a %1$s a las %2$s + + Telegram para Android fue actualizada. Novedades en la versión 2.9:\n\n- Instala y comparte packs de stickers personalizados como este: https://telegram.me/addstickers/Animals\n- Si eres un artista, crea packs de stickers personalizados usando nuestro bot @stickers.\n\n- Usa Telegram con Android Auto. + 536 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-it/strings.xml b/TMessagesProj/src/main/res/values-it/strings.xml index 8d827737c..2fd334137 100644 --- a/TMessagesProj/src/main/res/values-it/strings.xml +++ b/TMessagesProj/src/main/res/values-it/strings.xml @@ -39,7 +39,7 @@ Connessione… Aggiornamento… Nuova chat segreta - In attesa che %s si colleghi… + Aspettando che %s si colleghi… Chat segreta annullata Scambio delle chiavi di cifratura... %s si è unito alla tua chat segreta. @@ -70,7 +70,7 @@ Liberi %1$s di %2$s Errore sconosciuto Errore durante l\'accesso - Non ci sono ancora file... + Ancora nessun file... La dimensione del file non dovrebbe superare i %1$s Archiviazione non montata Trasferimento USB attivo @@ -89,13 +89,17 @@ %1$s sta inviando una foto... %1$s sta inviando un video... %1$s sta inviando un file... + registrando un audio... + inviando una foto... + inviando un video... + inviando un file... Hai una domanda\nsu Telegram? Scatta una foto Galleria Posizione Video File - Non ci sono ancora messaggi… + Ancora nessun messaggio qui… Messaggio inoltrato Da Nessuno recente @@ -110,7 +114,7 @@ Non lasciano traccia sui nostri server Hanno un timer di autodistruzione Non permettono l’inoltro - Sei stato espulso da questo gruppo + Sei stato rimosso da questo gruppo Hai lasciato il gruppo Elimina questo gruppo Elimina questa chat @@ -122,6 +126,8 @@ Timer di autodistruzione Notifiche di servizio Recupero le info del link... + Apri nel Browser + Copia URL %1$s ha impostato il timer di autodistruzione a %2$s Hai impostato il timer di autodistruzione a %1$s @@ -151,12 +157,13 @@ %1$s ha modificato la foto del gruppo %2$s %1$s ha invitato %3$s nel gruppo %2$s %1$s è rientrato nel gruppo %2$s - %1$s ha espulso %3$s dal gruppo %2$s - %1$s ti ha espulso dal gruppo %2$s + %1$s ha rimosso %3$s dal gruppo %2$s + %1$s ti ha rimosso dal gruppo %2$s %1$s ha lasciato il gruppo %2$s %1$s ha iniziato a usare Telegram! - %1$s,\nAbbiamo rilevato un accesso al tuo account da un nuovo dispositivo il %2$s\n\nDispositivo: %3$s\nPosizione: %4$s\n\nSe non sei stato tu, puoi andare su Impostazioni - Privacy e Sicurezza - Sessioni - Termina tutte le sessioni.\n\nSe pensi che qualcuno si sia collegato al tuo account contro il tuo volere, ti raccomandiamo di attivare la verifica in due passaggi nelle impostazioni di Privacy e Sicurezza.\n\nGrazie,\nil team di Telegram + %1$s,\nAbbiamo rilevato un accesso al tuo account da un nuovo dispositivo il %2$s\n\nDispositivo: %3$s\nPosizione: %4$s\n\nSe non sei stato tu, puoi andare su Impostazioni - Privacy e sicurezza - Sessioni - Termina tutte le sessioni.\n\nSe pensi che qualcuno si sia collegato al tuo account contro il tuo volere, ti raccomandiamo di attivare la verifica in due passaggi nelle impostazioni di Privacy e Sicurezza.\n\nGrazie,\nil team di Telegram %1$s ha aggiornato la foto del profilo + %1$s si è unito al gruppo %2$s tramite link di invito Rispondi Rispondi a %1$s Rispondi a %1$s @@ -182,13 +189,26 @@ Immetti il nome del gruppo Nome gruppo %1$d/%2$d membri + Vuoi unirti al gruppo \'%1$s\'? + Ci spiace, questo gruppo è già pieno. + Ci spiace, sembra che questo gruppo non esista. + Link copiato negli appunti + Invita nel gruppo tramite link + Link di invito + Sei sicuro di voler revocare questo link? Una volta fatto, nessuno potrà unirsi al gruppo utilizzandolo. + Il precedente link di invito è inattivo. Ne è appena stato creato uno nuovo. + Revoca + Revoca link + Copia link + Condividi link + Chiunque abbia Telegram installato, sarà in grado di aggiungersi al tuo gruppo aprendo il link. Media condivisi Impostazioni Aggiungi membro Elimina e lascia il gruppo Notifiche - Espelli dal gruppo + Rimuovi dal gruppo Condividi Aggiungi @@ -223,6 +243,21 @@ %1$s è disponibile. Nessuno Si è verificato un errore. + + Sticker + Grandi menti + Gli artisti sono invitati ad aggiungere i loro pacchetti di sticker usando il nostro bot @stickers.\n\nGli utenti possono aggiungere sticker premendo su di loro e scegliendo \"Aggiungi agli sticker\". + Aggiungi sticker + Aggiungi agli sticker + Sticker non trovati + Sticker rimossi + Nuovi sticker aggiunti + Nascondi + Mostra + Condividi + Copia link + Rimuovi + Ancora nessuno sticker Ripristina tutte le impostazioni di notifica predefinite Dimensione testo messaggi @@ -253,7 +288,7 @@ Solo se silenzioso Sfondo chat Messaggi - Spedisci con Invio + Spedisci con invio Termina le altre sessioni Eventi Un contatto ha iniziato a usare Telegram @@ -279,7 +314,7 @@ Solo con schermo acceso Solo con schermo spento Mostra sempre i popup - Contatore Badge + Contatore badge Breve Lunga Predefinito di sistema @@ -297,7 +332,7 @@ Alta Massima Mai - Ripeti Notifiche + Ripeti notifiche Puoi cambiare il tuo numero di telefono qui. Il tuo account e tutti i tuoi dati cloud — messaggi, file, contatti, etc. saranno trasferiti sul nuovo numero.\n\nImportante:]]> a tutti i tuoi contatti di Telegram verrà aggiunto il tuo nuovo numero]]> ai contatti, purché abbiano il tuo vecchio numero e tu non li abbia bloccati su Telegram. Tutti i tuoi contatti Telegram avranno il tuo nuovo numero tra i loro contatti, purché abbiano il tuo vecchio numero e tu non li abbia bloccati su Telegram. CAMBIA NUMERO @@ -311,6 +346,13 @@ No Suoni in-chat Predefinito + Notifiche intelligenti + Disabilitate + Suona al massimo %1$s in %2$s + Suona al massimo + volte + in + minuti Sessioni attive Sessione corrente @@ -350,15 +392,20 @@ Ibrido m di distanza km di distanza - Invia posizione - Condividi posizione + Invia la tua posizione attuale + Invia la posizione selezionata + Posizione + Precisione di %1$s + O SELEZIONA UN LUOGO Mostra tutti i file media Salva nella galleria %1$d di %2$d Galleria Tutte le foto + Tutti i video Ancora nessuna foto + Ancora nessun video Scarica prima il file Nessuna foto recente Nessuna GIF recente @@ -388,6 +435,11 @@ Annullare le modifiche? Cancellare la cronologia di ricerca? Pulisci + Foto + Video + Aggiungi una didascalia... + Didascalia foto + Didascalia video Verifica in due passaggi Imposta password aggiuntiva @@ -439,16 +491,16 @@ Hai attivato la verifica in due passaggi.\nAvrai bisogno della password che hai impostato per accedere al tuo account Telegram. La tua e-mail di recupero %1$s non è ancora attiva e attende la conferma. - Privacy e Sicurezza + Privacy e sicurezza Privacy - Ultimo Accesso + Ultimo accesso Tutti - I miei Contatti + I miei contatti Nessuno Tutti (-%1$d) - I miei Contatti (+%1$d) - I miei Contatti (-%1$d) - I miei Contatti (-%1$d, +%2$d) + I miei contatti (+%1$d) + I miei contatti (-%1$d) + I miei contatti (-%1$d, +%2$d) Nessuno (+%1$d) Sicurezza Elimina il mio account @@ -482,6 +534,7 @@ Fatto Apri Annulla + Chiudi Aggiungi Modifica Invia @@ -497,23 +550,26 @@ OK RITAGLIA - un1 ha espulso un2 + Ti sei unito al gruppo tramite link di invito + un1 si è unito al gruppo tramite link di invito + un1 ha rimosso un2 un1 ha lasciato il gruppo un1 ha aggiunto un2 un1 ha rimosso la foto del gruppo un1 ha cambiato la foto del gruppo un1 ha cambiato il nome del gruppo in un2 un1 ha creato il gruppo - Hai espulso un2 + Hai rimosso un2 Hai lasciato il gruppo Hai aggiunto un2 Hai rimosso la foto del gruppo Hai cambiato la foto del gruppo Hai cambiato il nome del gruppo in un2 Hai creato il gruppo - un1 ti ha espulso + un1 ti ha rimosso un1 ti ha aggiunto - un1 è ritentrato nel gruppo + un1 è rientrato nel gruppo + Sei rientrato nel gruppo Questo messaggio non è supportato sulla tua versione di Telegram. Aggiorna l\'applicazione per visualizzarlo: http://telegram.org/update Foto Video @@ -544,12 +600,12 @@ Inviare i messaggi a %1$s? Sei sicuro di volerti disconnettere?\n\nRicorda che puoi usare Telegram su tutti i tuoi device insieme.\n\nRicorda, disconnettersi elimina tutte le Chat Segrete. Terminare tutte le altre sessioni? - Eliminare il gruppo e uscire da esso? - Eliminare questa chat? + Sei sicuro di voler uscire ed eliminare il gruppo? + Sei sicuro di voler eliminare questa chat? Condividere le informazioni del proprio contatto? Bloccare questo contatto? Sbloccare questo contatto? - Eliminare questo contatto? + Sei sicuro di voler eliminare questo contatto? Iniziare una chat segreta? Sei sicuro di volere eliminare questa registrazione? Sei sicuro di volere eliminare la cronologia? @@ -664,6 +720,24 @@ %1$d utenti %1$d utenti %1$d utenti + %1$d volte + %1$d volta + %1$d volte + %1$d volte + %1$d volte + %1$d volte + %1$d metri + %1$d metro + %1$d metri + %1$d metri + %1$d metri + %1$d metri + %1$d sticker + %1$d sticker + %1$d sticker + %1$d sticker + %1$d sticker + %1$d sticker %1$d messaggi inoltrati Messaggio inoltrato @@ -730,4 +804,7 @@ HH:mm h:mm a %1$s alle %2$s + + Telegram per Android è stato aggiornato. Nuovo nella versione 2.9:\n\n- Installa e condividi pacchetti di sticker personalizzati come questo: https://telegram.me/addstickers/Animals\n- Se sei un artista, crea pacchetti di sticker utilizzando il nostro bot @stickers.\n\n- Usa Telegram con Android Auto. + 536 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-ko/strings.xml b/TMessagesProj/src/main/res/values-ko/strings.xml index e4c80afb8..e642f9979 100644 --- a/TMessagesProj/src/main/res/values-ko/strings.xml +++ b/TMessagesProj/src/main/res/values-ko/strings.xml @@ -89,6 +89,10 @@ %1$s님이 사진 보내는 중... %1$s님이 동영상 보내는 중... %1$s님이 파일 보내는 중... + 음성메시지 녹음 중... + 사진 전송 중.. + 동영상 전송 중.. + 파일 전송 중... 텔레그램에 관해\n궁금한 사항이 있나요? 사진 촬영 앨범 @@ -122,6 +126,8 @@ 자동삭제 타이머 설정 서비스 알림 링크 정보를 가져오는 중... + 브라우져에서 열기 + URL 복사 %1$s님이 자동삭제를 %2$s 후로 설정했습니다 자동삭제를 %1$s 후로 설정했습니다 @@ -157,6 +163,7 @@ %1$s님이 텔레그램에 가입했습니다! %1$s님,\n%2$s에 새 기기에서 회원님의 계정 로그인이 감지되었습니다. \n\n기기: %3$s\n위치: %4$s\n\n본인의 접속이 아니라면 \'설정\' 창에서 \'모든 세션 종료\' 기능을 실행하세요.\n\n만약 강제접속 의심이 되신다면 2단계 인증을 설정 - 개인정보 및 보안에서 설정할 수 있습니다.\n\n감사합니다.\n텔레그램 팀 %1$s님이 프로필 사진을 변경했습니다 + 초대링크를 타고 %1$s님께서 %2$s 그룹에 참여하셨습니다. 답장 %1$s 그룹에 답장하기 %1$s님에게 답장하기 @@ -182,6 +189,19 @@ 그룹 이름 입력 그룹 이름 대화상대 %1$d/%2$d + \'%1$s\' 그룹에 참여하시겠습니까? + 죄송합니다, 그룹방의 인원이 최대치입니다. + 죄송합니다, 그룹방이 더이상 존재하지 않습니다. + 클립보드로 링크가 복사되었습니다. + 링크를 통하여 그룹방에 초대하기 + 초대링크 + 초대링크를 폐지하시겠습니까? 진행하실 경우 해당 링크로 그룹방에 참여할 수 없게 됩니다. + 기존 초대링크는 비활성화 되었습니다. 새로운 링크가 생성되었습니다. + 폐지하기 + 링크 폐지 + 링크 복사 + 링크 공유 + 텔레그램이 설치된 분들은 링크를 타고 그룹방에 참여가 가능합니다. 공유한 미디어 설정 @@ -223,6 +243,21 @@ %1$s: 사용 가능합니다. 없음 오류가 발생했습니다. + + 스티커 + Great Minds + \@stickers 봇을 통하여 누구든지 스스로 제작한 스티커를 등록 할 수 있습니다.\n\n스티커는 더블탭하여 \"스티커 추가\" 를 통하여 추가할 수 있습니다. + 스티커 추가 + 스티커 추가 + 스티커를 찾을 수 없음 + 스티커 제거됨 + 새로운 스티커가 추가되었습니다. + 숨김 + 보기 + 공유 + 링크 복사 + 삭제 + 스티커가 아직 없음 모든 알림 설정이 초기화되었습니다 채팅 글자 크기 @@ -311,6 +346,13 @@ 채팅중 소리 설정 기본값 + 스마트 알림 + 비활성화됨 + 최대 %1$s번, %2$s번 이내 알림 + 알림 최대치 + + 이내 + 활성화된 세션 현재 세션 @@ -350,15 +392,20 @@ 혼합 m 떨어짐 km 떨어짐 - 위치 보내기 - 위치 공유 + 현재 위치 전송 + 선택한 위치 전송 + 위치 + %1$s 반경 내 정확함 + 혹은 위치를 선택 모든 미디어 보기 앨범에 저장 %1$d / %2$d 앨범 모든 사진 + 모든 동영상 사진이 없습니다. + 동영상이 아직 없음 사진/동영상을 먼저 다운로드하세요 최근 사진 없음 최근에 검색한 GIF @@ -388,6 +435,11 @@ 변경을 취소하시겠습니까? 검색기록을 지우시겠습니까? 지우기 + 사진 + 동영상 + 설명 추가... + 사진 설명 + 동영상 설명 2단계 인증 개별 비밀번호 설정 @@ -482,6 +534,7 @@ 완료 열기 취소 + 닫기 추가 편집 보내기 @@ -497,6 +550,8 @@ 확인 자르기 + 초대링크를 타고 그룹에 참여하였습니다. + 초대링크를 타고 그룹에 un1님이 참여하였습니다. un1님이 un2님을 추방했습니다 un1님이 퇴장했습니다 un1님이 un2님을 초대했습니다 @@ -514,6 +569,7 @@ un1님이 추방했습니다 un1님이 그룹에 초대했습니다 un1 님께서 그룹에 돌아오셨습니다 + 그룹에 돌아오셨습니다. 이 메시지는 사용 중인 텔레그램의 버전이 낮아 지원하지 않습니다. 앱을 업데이트 하세요: http://telegram.org/update 사진 동영상 @@ -664,6 +720,24 @@ %1$d명의 대화상대 %1$d명의 대화상대 %1$d명의 대화상대 + %1$d 번 + %1$d 번 + %1$d 번 + %1$d 번 + %1$d 번 + %1$d 번 + %1$d 미터 + %1$d 미터 + %1$d 미터 + %1$d 미터 + %1$d 미터 + %1$d 미터 + 스티커 %1$d개 + 스티커 %1$d개 + 스티커 %1$d개 + 스티커 %1$d개 + 스티커 %1$d개 + 스티커 %1$d개 %1$d 개의 전달된 메시지 전달된 메시지 @@ -730,4 +804,7 @@ HH:mm a h:mm %1$s %2$s + + 텔레그램 안드로이드 버전이 업데이트 되었습니다. 새로운 버전은 2.9 입니다:\n\n - 아래와 같은 커스텀 스티커 설치 및 공유 : https://telegram.me/addstickers/Animals\n - 커스텀 스티커를 신규로 생성하여 등록할 수 있는 @stickers 봇 활용\n\n - Android Auto와 텔레그램 호환 + 536 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-nl/strings.xml b/TMessagesProj/src/main/res/values-nl/strings.xml index d652346b1..990c87af5 100644 --- a/TMessagesProj/src/main/res/values-nl/strings.xml +++ b/TMessagesProj/src/main/res/values-nl/strings.xml @@ -89,6 +89,10 @@ %1$s verstuurt een foto %1$s verstuurt een video %1$s verstuurt een bestand + geluid opnemen + foto versturen + video versturen + bestand versturen Heb je een vraag\nover Telegram? Foto maken Galerij @@ -122,6 +126,8 @@ Zelfvernietigingstimer instellen Servicemeldingen Link-preview ophalen... + Openen in browser + Link kopiëren %1$s heeft de zelfvernietigingstimer ingesteld op %2$s Je hebt de zelfvernietigingstimer ingesteld op %1$s @@ -157,6 +163,7 @@ %1$s heeft nu Telegram! %1$s,\nEr is op je account ingelogd vanaf een nieuw apparaat op %2$s\n\nApparaat: %3$s\nLocatie: %4$s\n\nAls jij dit niet was, kun je die sessie beëindigen via Instellingen - Privacy en veiligheid - Sessies.\n\nAls je dat denkt dat iemand anders zonder jouw toestemming is ingelogd kun je twee-staps-verificatie activeren via instellingen - privacy en veiligheid .\n\nBedankt,\nHet Telegram-team %1$s heeft zijn/haar profielfoto gewijzigd + %1$s neemt deel aan de groep %2$s via uitnodigingslink Antwoord Antwoord op %1$s Antwoord op %1$s @@ -182,6 +189,19 @@ Groepsnaam Groepsnaam %1$d/%2$d deelnemers + Wil je deelnemen aan de groep \'%1$s\'? + Sorry, deze groep is al vol. + Sorry, deze groep bestaat niet. + Link gekopieerd naar klembord. + Uitnodigingslink sturen + Uitnodigingslink + Deze link echt intrekken? Na intrekken kan niemand meer lid worden met de oude link. + De oude link is nu inactief. Een nieuwe link is aangemaakt. + Intrekken + Link intrekken + Link kopiëren + Link delen + Andere Telegram-gebruikers kunnen aan je groep deelnemen door deze link te openen. Gedeelde media Instellingen @@ -223,6 +243,23 @@ %1$s is beschikbaar. Geen Er is een fout opgetreden. + + Stickers + Grote geesten + Ontwerpers kunnen stickerbundels toevoegen via onze bot: @stickers. + +Gebruikers kunnen met een tik stickers toevoegen via \"Toevoegen aan stickers\". + Stickers toevoegen + Toevoegen aan stickers + Stickers niet gevonden + Stickers verwijderd + Nieuwe stickers toegevoegd + Verbergen + Weergeven + Delen + Link kopiëren + Verwijder + Nog geen stickers Meldingen gereset Tekstgrootte berichten @@ -311,6 +348,13 @@ Uit Chatgeluiden Standaard + Slimme meldingen + Uitgeschakeld + Geluid maximaal %1$s per %2$s + Geluid maximaal + keer + binnen + minuten Actieve sessies Huidige sessie @@ -350,15 +394,20 @@ Hybride m hiervandaan km hiervandaan - Locatie versturen - Locatie delen + Huidige locatie sturen + Geselecteerde locatie sturen + Locatie + Nauwkeurig tot op %1$s + OF KIES EEN PLEK Alle media weergeven Opslaan in galerij %1$d van %2$d Galerij Alle foto\'s + Alle video\'s Nog geen foto\'s + Nog geen video\'s Download media eerst Niets recents Niets recents @@ -388,6 +437,11 @@ Wijzigingen negeren? Zoekgeschiedenis wissen? Wissen + Foto\'s + Video + Onderschrift toevoegen + Foto-onderschrift + Video-onderschrift Twee-staps-verificatie Extra wachtwoord instellen @@ -482,6 +536,7 @@ Gereed Openen Annuleren + Sluit Toevoegen Wijzig Stuur @@ -497,6 +552,8 @@ OK BIJSNIJDEN + Je neemt deel aan de groep via uitnodigingslink + un1 neemt deel aan de groep via uitnodigingslink un1 heeft un2 verwijderd un1 heeft de groep verlaten un1 heeft un2 toegevoegd @@ -514,6 +571,7 @@ un1 heeft je verwijderd un1 heeft je toegevoegd un1 is terug in de groep + Je keerde terug naar de groep Dit bericht wordt niet ondersteund door jouw versie van Telegram. Werk Telegram bij om dit bericht te bekijken: http://telegram.org/update Foto Video @@ -664,6 +722,24 @@ %1$d gebruikers %1$d gebruikers %1$d gebruikers + %1$d keer + %1$d keer + %1$d keer + %1$d keer + %1$d keer + %1$d keer + %1$d meter + %1$d meter + %1$d meter + %1$d meter + %1$d meter + %1$d meter + %1$d stickers + %1$d sticker + %1$d stickers + %1$d stickers + %1$d stickers + %1$d stickers Bijlage: %1$d berichten Bijlage: 1 bericht @@ -730,4 +806,7 @@ HH:mm h:mm a %1$s om %2$s + + Telegram voor Android is geüpdatet. Nieuw in versie 2.9:\n\n- Deel en installeer stickerbundels zoals deze: https://telegram.me/addstickers/Animals\n- Ben je een ontwerper? Maak stickerbundels met onze bot: @stickers.\n\n- Gebruik Telegram met Android Auto. + 536 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-pt-rBR/strings.xml b/TMessagesProj/src/main/res/values-pt-rBR/strings.xml index b0d1b594a..ea42ce566 100644 --- a/TMessagesProj/src/main/res/values-pt-rBR/strings.xml +++ b/TMessagesProj/src/main/res/values-pt-rBR/strings.xml @@ -85,10 +85,14 @@ escrevendo... está escrevendo... estão escrevendo... - %1$s está gravando uma mensagem... + %1$s está gravando um áudio... %1$s está enviando uma foto... %1$s está enviando um vídeo... %1$s está enviando um arquivo... + gravando áudio... + enviando foto... + enviando vídeo... + enviando arquivo... Tem alguma dúvida\nsobre o Telegram? Tirar foto Galeria @@ -122,6 +126,8 @@ Definir timer de autodestruição Notificações de serviço Obtendo informações... + Abrir no Navegador + Copiar URL %1$s estabeleceu o tempo de autodestruição para %2$s Você estabeleceu o tempo de autodestruição para %1$s @@ -157,6 +163,7 @@ %1$s entrou para o Telegram! %1$s,\nNós detectamos que alguém acessou a sua conta a partir de um novo aparelho em %2$s\n\nAparelho: %3$s\nLocalização: %4$s\n\nSe não foi você, você pode ir em Configurações - Provacidade e Segurança - Sessões, e terminar aquela sessão.\n\nSe você acha que alguém acessou a sua conta contra a sua vontade, você pode habilitar a verificação em duas etapas nas configurações de Privacidade e Segurança.\n\nAtenciosamente,\nEquipe Telegram %1$s atualizou a foto do perfil + %1$s entrou para o grupo %2$s via link de convite Responder Responder para %1$s Responder para %1$s @@ -182,6 +189,19 @@ Digite o nome do grupo Nome do grupo %1$d/%2$d membros + Você deseja entrar no grupo \'%1$s\'? + Desculpe, este grupo já está lotado. + Desculpe, este grupo não existe. + Link copiado para área de transferência + Convidar para o Grupo via Link + Link de Convite + Você tem certeza que deseja desativar o link? Uma vez feito, ninguém conseguirá entrar no grupo usando-o. + Este link de convite está inativo. Um novo link foi gerado. + Desativar + Desativar Link + Copiar Link + Compartilhar Link + Qualquer um com Telegram instalado poderá entrar no seu grupo abrindo este link. Mídia Compartilhada Configurações @@ -223,6 +243,21 @@ %1$s está disponível. Nenhum Ocorreu um erro. + + Stickers + Grandes Mentes + Artistas são bem vindos a adicionar seus próprios pacotes de stickers usando o @stickers bot.\n\nUsuários podem adicionar stickers com um clique sobre eles e então escolher \"Adicionar aos Stickers\". + Adicionar Stickers + Adicionar aos Stickers + Stickers não encontrados + Stickers removidos + Novos stickers adicionados + Esconder + Mostrar + Compartilhar + Copiar link + Remover + Nenhum sticker Restaurar todas as configurações de notificação Tamanho do texto nas mensagens @@ -261,7 +296,7 @@ Idioma Por favor compreenda que o Suporte do Telegram é feito por voluntários. Tentamos responder o mais rápido possível, mas pode demorar um pouco.
]]>Por favor acesse o FAQ do Telegram]]>: temos respostas para algumas questões, assim como dicas importantes à resolução de problemas]]>.
Pergunte a um voluntário - FAQ do Telegram + Perguntas frequentes https://telegram.org/faq Apagar localização? Arquivo de localização incorreto @@ -311,6 +346,13 @@ Desativado Sons no Chat Padrão + Notificações Inteligentes + Desativado + Tocar no máximo %1$s a cada %2$s + Tocar no máximo + vezes + a cada + minutos Sessões Ativas Sessão atual @@ -350,15 +392,20 @@ Híbrido m de distância km de distância - Enviar Localização - Compartilhar Localização + Enviar sua localização atual + Enviar localização selecionada + Localização + Precisão de %1$s + OU ESCOLHA UM LUGAR Mostrar todas as mídias Salvar na galeria %1$d de %2$d Galeria Todas as Fotos + Todos os Vídeos Ainda não há fotos + Nenhum vídeo ainda Baixar o vídeo primeiro Nenhuma foto recente Nenhum GIF recente @@ -388,6 +435,11 @@ Descartar mudanças? Limpar histórico de busca? Limpar + Fotos + Vídeo + Adicionar legenda... + Legenda da Foto + Legenda do Vídeo Verificação em duas etapas Configurar senha adicional @@ -424,7 +476,7 @@ O código de recuperação foi enviado para o e-mail fornecido: \n\n%1$s Por favor, verifique o seu e-mail e digite aqui o código de 6 dígitos recebido. Está tendo problemas para acessar seu e-mail %1$s? - Se você não puder acessar o seu e-mail, as suas únicas opções são são lembrar a senha ou apagar a sua conta. + Se você não puder acessar o seu e-mail, as suas únicas opções são lembrar a senha ou apagar a sua conta. APAGAR MINHA CONTA Se você prosseguir e apagar a sua conta, você perderá todos os seus chats e mensagens, assim como todas as suas mídias e arquivos compartilhados. Aviso @@ -482,6 +534,7 @@ Concluído Abrir Cancelar + Fechar Adicionar Editar Enviar @@ -497,6 +550,8 @@ OK CORTAR + Você entrou para o grupo via link de convite + un1 entrou para o grupo via link de convite un1 removeu un2 un1 saiu do grupo un1 adicionou un2 @@ -514,6 +569,7 @@ un1 removeu você un1 adicionou você un1 retornou ao grupo + Você retornou ao grupo Esta mensagem não é suportada na sua versão do Telegram. Para visualiza-la atualize seu aplicativo em http://telegram.org/update Foto Vídeo @@ -664,6 +720,24 @@ %1$d usuários %1$d usuários %1$d usuários + %1$d vezes + %1$d vez + %1$d vezes + %1$d vezes + %1$d vezes + %1$d vezes + %1$d metros + %1$d metro + %1$d metros + %1$d metros + %1$d metros + %1$d metros + %1$d stickers + %1$d sticker + %1$d stickers + %1$d stickers + %1$d stickers + %1$d stickers %1$d mensagens encaminhadas Mensagem encaminhada @@ -730,4 +804,7 @@ HH:mm h:mm a %1$s às %2$s + + Seu Telegram para Android acaba de ser atualizado. Novidades da versão 2.9:\n\n- Instale e compartilhe pacotes de stickers customizados como esse: https://telegram.me/addstickers/Animals\n- Se você é um artista, crie stickers customizados usando o @stickers bot.\n\n- Use Telegram com Android Auto. + 536 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-pt-rPT/strings.xml b/TMessagesProj/src/main/res/values-pt-rPT/strings.xml index dd6aca2f4..06a50d418 100644 --- a/TMessagesProj/src/main/res/values-pt-rPT/strings.xml +++ b/TMessagesProj/src/main/res/values-pt-rPT/strings.xml @@ -4,9 +4,9 @@ Telegram - Português (Portugal) - Portuguese (Portugal) - pt_PT + Português (Brasil) + Português (Brasil) + pt_BR Seu número Confirme o código de seu país e preencha seu número de telefone. @@ -85,10 +85,14 @@ escrevendo... está escrevendo... estão escrevendo... - %1$s está gravando uma mensagem... + %1$s está gravando um áudio... %1$s está enviando uma foto... %1$s está enviando um vídeo... %1$s está enviando um arquivo... + gravando áudio... + enviando foto... + enviando vídeo... + enviando arquivo... Tem alguma dúvida\nsobre o Telegram? Tirar foto Galeria @@ -122,6 +126,8 @@ Definir timer de autodestruição Notificações de serviço Obtendo informações... + Abrir no Navegador + Copiar URL %1$s estabeleceu o tempo de autodestruição para %2$s Você estabeleceu o tempo de autodestruição para %1$s @@ -157,6 +163,7 @@ %1$s entrou para o Telegram! %1$s,\nNós detectamos que alguém acessou a sua conta a partir de um novo aparelho em %2$s\n\nAparelho: %3$s\nLocalização: %4$s\n\nSe não foi você, você pode ir em Configurações - Provacidade e Segurança - Sessões, e terminar aquela sessão.\n\nSe você acha que alguém acessou a sua conta contra a sua vontade, você pode habilitar a verificação em duas etapas nas configurações de Privacidade e Segurança.\n\nAtenciosamente,\nEquipe Telegram %1$s atualizou a foto do perfil + %1$s entrou para o grupo %2$s via link de convite Responder Responder para %1$s Responder para %1$s @@ -182,6 +189,19 @@ Digite o nome do grupo Nome do grupo %1$d/%2$d membros + Você deseja entrar no grupo \'%1$s\'? + Desculpe, este grupo já está lotado. + Desculpe, este grupo não existe. + Link copiado para área de transferência + Convidar para o Grupo via Link + Link de Convite + Você tem certeza que deseja desativar o link? Uma vez feito, ninguém conseguirá entrar no grupo usando-o. + Este link de convite está inativo. Um novo link foi gerado. + Desativar + Desativar Link + Copiar Link + Compartilhar Link + Qualquer um com Telegram instalado poderá entrar no seu grupo abrindo este link. Mídia Compartilhada Configurações @@ -223,6 +243,21 @@ %1$s está disponível. Nenhum Ocorreu um erro. + + Stickers + Grandes Mentes + Artistas são bem vindos a adicionar seus próprios pacotes de stickers usando o @stickers bot.\n\nUsuários podem adicionar stickers com um clique sobre eles e então escolher \"Adicionar aos Stickers\". + Adicionar Stickers + Adicionar aos Stickers + Stickers não encontrados + Stickers removidos + Novos stickers adicionados + Esconder + Mostrar + Compartilhar + Copiar link + Remover + Nenhum sticker Restaurar todas as configurações de notificação Tamanho do texto nas mensagens @@ -261,7 +296,7 @@ Idioma Por favor compreenda que o Suporte do Telegram é feito por voluntários. Tentamos responder o mais rápido possível, mas pode demorar um pouco.
]]>Por favor acesse o FAQ do Telegram]]>: temos respostas para algumas questões, assim como dicas importantes à resolução de problemas]]>.
Pergunte a um voluntário - FAQ do Telegram + Perguntas frequentes https://telegram.org/faq Apagar localização? Arquivo de localização incorreto @@ -311,6 +346,13 @@ Desativado Sons no Chat Padrão + Notificações Inteligentes + Desativado + Tocar no máximo %1$s a cada %2$s + Tocar no máximo + vezes + a cada + minutos Sessões Ativas Sessão atual @@ -350,15 +392,20 @@ Híbrido m de distância km de distância - Enviar Localização - Compartilhar Localização + Enviar sua localização atual + Enviar localização selecionada + Localização + Precisão de %1$s + OU ESCOLHA UM LUGAR Mostrar todas as mídias Salvar na galeria %1$d de %2$d Galeria Todas as Fotos + Todos os Vídeos Ainda não há fotos + Nenhum vídeo ainda Baixar o vídeo primeiro Nenhuma foto recente Nenhum GIF recente @@ -388,6 +435,11 @@ Descartar mudanças? Limpar histórico de busca? Limpar + Fotos + Vídeo + Adicionar legenda... + Legenda da Foto + Legenda do Vídeo Verificação em duas etapas Configurar senha adicional @@ -424,7 +476,7 @@ O código de recuperação foi enviado para o e-mail fornecido: \n\n%1$s Por favor, verifique o seu e-mail e digite aqui o código de 6 dígitos recebido. Está tendo problemas para acessar seu e-mail %1$s? - Se você não puder acessar o seu e-mail, as suas únicas opções são são lembrar a senha ou apagar a sua conta. + Se você não puder acessar o seu e-mail, as suas únicas opções são lembrar a senha ou apagar a sua conta. APAGAR MINHA CONTA Se você prosseguir e apagar a sua conta, você perderá todos os seus chats e mensagens, assim como todas as suas mídias e arquivos compartilhados. Aviso @@ -482,6 +534,7 @@ Concluído Abrir Cancelar + Fechar Adicionar Editar Enviar @@ -497,6 +550,8 @@ OK CORTAR + Você entrou para o grupo via link de convite + un1 entrou para o grupo via link de convite un1 removeu un2 un1 saiu do grupo un1 adicionou un2 @@ -514,6 +569,7 @@ un1 removeu você un1 adicionou você un1 retornou ao grupo + Você retornou ao grupo Esta mensagem não é suportada na sua versão do Telegram. Para visualiza-la atualize seu aplicativo em http://telegram.org/update Foto Vídeo @@ -664,6 +720,24 @@ %1$d usuários %1$d usuários %1$d usuários + %1$d vezes + %1$d vez + %1$d vezes + %1$d vezes + %1$d vezes + %1$d vezes + %1$d metros + %1$d metro + %1$d metros + %1$d metros + %1$d metros + %1$d metros + %1$d stickers + %1$d sticker + %1$d stickers + %1$d stickers + %1$d stickers + %1$d stickers %1$d mensagens encaminhadas Mensagem encaminhada @@ -730,4 +804,7 @@ HH:mm h:mm a %1$s às %2$s + + Seu Telegram para Android acaba de ser atualizado. Novidades da versão 2.9:\n\n- Instale e compartilhe pacotes de stickers customizados como esse: https://telegram.me/addstickers/Animals\n- Se você é um artista, crie stickers customizados usando o @stickers bot.\n\n- Use Telegram com Android Auto. + 536
\ No newline at end of file diff --git a/TMessagesProj/src/main/res/values/colors.xml b/TMessagesProj/src/main/res/values/colors.xml index 31a03a7c9..b4eac3ecb 100755 --- a/TMessagesProj/src/main/res/values/colors.xml +++ b/TMessagesProj/src/main/res/values/colors.xml @@ -1,5 +1,4 @@ - #6633B5E5 #dcdcdc \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values/strings.xml b/TMessagesProj/src/main/res/values/strings.xml index 847bff777..85ef4036e 100644 --- a/TMessagesProj/src/main/res/values/strings.xml +++ b/TMessagesProj/src/main/res/values/strings.xml @@ -89,6 +89,10 @@ %1$s is sending photo... %1$s is sending video... %1$s is sending file... + recording audio... + sending photo... + sending video... + sending file... Got a question\nabout Telegram? Take photo Gallery @@ -122,6 +126,8 @@ Set self-destruct timer Service notifications Getting Link Info... + Open in Browser + Copy URL %1$s set the self-destruct timer to %2$s You set the self-destruct timer to %1$s @@ -157,6 +163,7 @@ %1$s joined Telegram! %1$s,\nWe detected a login into your account from a new device on %2$s\n\nDevice: %3$s\nLocation: %4$s\n\nIf this wasn\'t you, you can go to Settings - Privacy and Security - Sessions and terminate that session.\n\nIf you think that somebody logged in to your account against your will, you can enable two-step verification in Privacy and Security settings.\n\nSincerely,\nThe Telegram Team %1$s updated profile photo + %1$s joined to the group %2$s via invite link Reply Reply to %1$s Reply to %1$s @@ -182,6 +189,19 @@ Enter group name Group name %1$d/%2$d members + Do you want to join the group \'%1$s\'? + Sorry, this group is already full. + Sorry, this group does not seem to exist. + Link copied to clipboard + Invite to Group via Link + Invite Link + Are you sure you want to revoke this link? Once you do, no one will be able to join the group using it. + The previous invite link is now inactive. A new link has been generated. + Revoke + Revoke Link + Copy Link + Share Link + Anyone who has Telegram installed will be able to join your group by following this link. Shared Media Settings @@ -223,6 +243,21 @@ %1$s is available. None An error occurred. + + Stickers + Great Minds + Artists are welcome to add their own sticker packs using our @stickers bot.\n\nUsers can add stickers by tapping on them and choosing \"Add to Stickers\". + Add Stickers + Add to Stickers + Stickers not found + Stickers removed + New stickers added + Hide + Show + Share + Copy link + Remove + No stickers yet Reset all notification settings to default Messages Text Size @@ -311,6 +346,13 @@ Off In-Chat Sounds Default + Smart Notifications + Disabled + Sound at most %1$s within %2$s + Sound at most + times + within + minutes Active Sessions Current session @@ -350,15 +392,20 @@ Hybrid m away km away - Send Location - Share Location + Send your current location + Send selected location + Location + Accurate to %1$s + OR CHOOSE A PLACE Show all media Save to gallery %1$d of %2$d Gallery All Photos + All Videos No photos yet + No videos yet Please download media first No recent photos No recent GIFs @@ -388,6 +435,11 @@ Discard changes? Clear search history? Clear + Photos + Video + Add a caption... + Photo Caption + Video Caption Two-Step Verification Set Additional Password @@ -482,6 +534,7 @@ Done Open Cancel + Close Add Edit Send @@ -497,6 +550,8 @@ OK CROP + You joined the group via invite link + un1 joined the group via invite link un1 removed un2 un1 left group un1 added un2 @@ -514,6 +569,7 @@ un1 removed you un1 added you un1 returned to the group + You returned to the group This message is not supported on your version of Telegram. Update the app to view: http://telegram.org/update Photo Video @@ -664,6 +720,24 @@ %1$d users %1$d users %1$d users + %1$d times + %1$d time + %1$d times + %1$d times + %1$d times + %1$d times + %1$d meters + %1$d meter + %1$d meters + %1$d meters + %1$d meters + %1$d meters + %1$d stickers + %1$d sticker + %1$d stickers + %1$d stickers + %1$d stickers + %1$d stickers %1$d forwarded messages Forwarded message @@ -730,4 +804,7 @@ HH:mm h:mm a %1$s at %2$s + + Telegram for Android has been updated. New in version 2.9:\n\n- Install and share custom sticker sets like this one: https://telegram.me/addstickers/Animals\n- If you\'re an artist, create custom sticker sets using our @stickers bot.\n\n- Use Telegram with Android Auto. + 536 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/xml/auth.xml b/TMessagesProj/src/main/res/xml/auth.xml index 7856d0c83..cc285409e 100644 --- a/TMessagesProj/src/main/res/xml/auth.xml +++ b/TMessagesProj/src/main/res/xml/auth.xml @@ -1,7 +1,7 @@ + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/xml/sync_contacts.xml b/TMessagesProj/src/main/res/xml/sync_contacts.xml index 9ec405a98..9e9962e68 100644 --- a/TMessagesProj/src/main/res/xml/sync_contacts.xml +++ b/TMessagesProj/src/main/res/xml/sync_contacts.xml @@ -1,3 +1,3 @@ \ No newline at end of file + android:accountType="org.telegram.messenger"/> \ No newline at end of file