update to 9.1.0

This commit is contained in:
xaxtix 2022-11-05 16:34:47 +04:00
parent e9a35cea54
commit 67b1fd17e6
318 changed files with 46849 additions and 16511 deletions

View File

@ -1,3 +1,5 @@
import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform
apply plugin: 'com.android.library'
repositories {
@ -42,6 +44,7 @@ dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
}
def isWindows = String.valueOf(DefaultNativePlatform.currentOperatingSystem.toFamilyName() == OperatingSystemFamily.WINDOWS)
android {
compileSdkVersion 31
buildToolsVersion '31.0.0'
@ -102,7 +105,8 @@ android {
ndk.debugSymbolLevel = 'FULL'
buildConfigField "String", "APP_CENTER_HASH", "\"\""
buildConfigField "boolean", "DEBUG_VERSION", "true"
buildConfigField "boolean", "DEBUG_PRIVATE_VERSION", "false"
buildConfigField "boolean", "DEBUG_PRIVATE_VERSION", "true"
buildConfigField "boolean", "BUILD_HOST_IS_WINDOWS", isWindows
}
HA_private {
@ -115,6 +119,7 @@ android {
buildConfigField "String", "APP_CENTER_HASH", "\"" + getProps("APP_CENTER_HASH_PRIVATE") + "\""
buildConfigField "boolean", "DEBUG_VERSION", "true"
buildConfigField "boolean", "DEBUG_PRIVATE_VERSION", "true"
buildConfigField "boolean", "BUILD_HOST_IS_WINDOWS", isWindows
}
HA_public {
@ -127,6 +132,7 @@ android {
buildConfigField "String", "APP_CENTER_HASH", "\"" + getProps("APP_CENTER_HASH_PUBLIC") + "\""
buildConfigField "boolean", "DEBUG_VERSION", "true"
buildConfigField "boolean", "DEBUG_PRIVATE_VERSION", "false"
buildConfigField "boolean", "BUILD_HOST_IS_WINDOWS", isWindows
}
standalone {
@ -139,6 +145,7 @@ android {
buildConfigField "String", "APP_CENTER_HASH", "\"\""
buildConfigField "boolean", "DEBUG_VERSION", "false"
buildConfigField "boolean", "DEBUG_PRIVATE_VERSION", "false"
buildConfigField "boolean", "BUILD_HOST_IS_WINDOWS", isWindows
}
release {
@ -152,6 +159,7 @@ android {
buildConfigField "String", "APP_CENTER_HASH", "\"\""
buildConfigField "boolean", "DEBUG_VERSION", "false"
buildConfigField "boolean", "DEBUG_PRIVATE_VERSION", "false"
buildConfigField "boolean", "BUILD_HOST_IS_WINDOWS", isWindows
}
}
}

View File

@ -31,6 +31,86 @@
android:requestLegacyExternalStorage="true"
tools:replace="android:supportsRtl">
<activity-alias
android:enabled="false"
android:name="org.telegram.messenger.VintageIcon"
android:targetActivity="org.telegram.ui.LaunchActivity"
android:icon="@mipmap/icon_6_launcher_sa"
android:roundIcon="@mipmap/icon_6_launcher_sa"
android:exported="true"
tools:node="replace">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:enabled="false"
android:name="org.telegram.messenger.AquaIcon"
android:targetActivity="org.telegram.ui.LaunchActivity"
android:icon="@mipmap/icon_4_launcher_sa"
android:roundIcon="@mipmap/icon_4_launcher_sa"
android:exported="true"
tools:node="replace">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:enabled="false"
android:name="org.telegram.messenger.PremiumIcon"
android:targetActivity="org.telegram.ui.LaunchActivity"
android:icon="@mipmap/icon_3_launcher_sa"
android:roundIcon="@mipmap/icon_3_launcher_sa"
android:exported="true"
tools:node="replace">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:enabled="false"
android:name="org.telegram.messenger.TurboIcon"
android:targetActivity="org.telegram.ui.LaunchActivity"
android:icon="@mipmap/icon_5_launcher_sa"
android:roundIcon="@mipmap/icon_5_launcher_sa"
android:exported="true"
tools:node="replace">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:enabled="false"
android:name="org.telegram.messenger.NoxIcon"
android:targetActivity="org.telegram.ui.LaunchActivity"
android:icon="@mipmap/icon_2_launcher_sa"
android:roundIcon="@mipmap/icon_2_launcher_sa"
android:exported="true"
tools:node="replace">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
</intent-filter>
</activity-alias>
<meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="AIzaSyA-t0jLPjUt2FxrA8VPK2EiYHcYcboIR6k" />
<service

File diff suppressed because it is too large Load Diff

View File

@ -43,7 +43,30 @@ extern "C" {
/*
** Provide the ability to override linkage features of the interface.
** Facilitate override of interface linkage and calling conventions.
** Be aware that these macros may not be used within this particular
** translation of the amalgamation and its associated header file.
**
** The SQLITE_EXTERN and SQLITE_API macros are used to instruct the
** compiler that the target identifier should have external linkage.
**
** The SQLITE_CDECL macro is used to set the calling convention for
** public functions that accept a variable number of arguments.
**
** The SQLITE_APICALL macro is used to set the calling convention for
** public functions that accept a fixed number of arguments.
**
** The SQLITE_STDCALL macro is no longer used and is now deprecated.
**
** The SQLITE_CALLBACK macro is used to set the calling convention for
** function pointers.
**
** The SQLITE_SYSAPI macro is used to set the calling convention for
** functions provided by the operating system.
**
** Currently, the SQLITE_CDECL, SQLITE_APICALL, SQLITE_CALLBACK, and
** SQLITE_SYSAPI macros are used only when building for environments
** that require non-default calling conventions.
*/
#ifndef SQLITE_EXTERN
# define SQLITE_EXTERN extern
@ -123,9 +146,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION "3.35.4"
#define SQLITE_VERSION_NUMBER 3035004
#define SQLITE_SOURCE_ID "2021-04-02 15:20:15 5d4c65779dab868b285519b19e4cf9d451d50c6048f06f653aa701ec212df45e"
#define SQLITE_VERSION "3.39.3"
#define SQLITE_VERSION_NUMBER 3039003
#define SQLITE_SOURCE_ID "2022-09-05 11:02:23 4635f4a69c8c2a8df242b384a992aea71224e39a2ccab42d8c0b0602f1e826e8"
/*
** CAPI3REF: Run-Time Library Version Numbers
@ -537,12 +560,13 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8))
#define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT |(10<<8))
#define SQLITE_CONSTRAINT_PINNED (SQLITE_CONSTRAINT |(11<<8))
#define SQLITE_CONSTRAINT_DATATYPE (SQLITE_CONSTRAINT |(12<<8))
#define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8))
#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8))
#define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8))
#define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8))
#define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8))
#define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8))
#define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) /* internal use only */
/*
** CAPI3REF: Flags For File Open Operations
@ -550,6 +574,19 @@ SQLITE_API int sqlite3_exec(
** These bit values are intended for use in the
** 3rd parameter to the [sqlite3_open_v2()] interface and
** in the 4th parameter to the [sqlite3_vfs.xOpen] method.
**
** Only those flags marked as "Ok for sqlite3_open_v2()" may be
** used as the third argument to the [sqlite3_open_v2()] interface.
** The other flags have historically been ignored by sqlite3_open_v2(),
** though future versions of SQLite might change so that an error is
** raised if any of the disallowed bits are passed into sqlite3_open_v2().
** Applications should not depend on the historical behavior.
**
** Note in particular that passing the SQLITE_OPEN_EXCLUSIVE flag into
** [sqlite3_open_v2()] does *not* cause the underlying database file
** to be opened using O_EXCL. Passing SQLITE_OPEN_EXCLUSIVE into
** [sqlite3_open_v2()] has historically be a no-op and might become an
** error in future versions of SQLite.
*/
#define SQLITE_OPEN_READONLY 0x00000001 /* Ok for sqlite3_open_v2() */
#define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlite3_open_v2() */
@ -572,6 +609,7 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for sqlite3_open_v2() */
#define SQLITE_OPEN_WAL 0x00080000 /* VFS only */
#define SQLITE_OPEN_NOFOLLOW 0x01000000 /* Ok for sqlite3_open_v2() */
#define SQLITE_OPEN_EXRESCODE 0x02000000 /* Extended result codes */
/* Reserved: 0x00F00000 */
/* Legacy compatibility: */
@ -1128,6 +1166,23 @@ struct sqlite3_io_methods {
** file to the database file, but before the *-shm file is updated to
** record the fact that the pages have been checkpointed.
** </ul>
**
** <li>[[SQLITE_FCNTL_EXTERNAL_READER]]
** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect
** whether or not there is a database client in another process with a wal-mode
** transaction open on the database or not. It is only available on unix.The
** (void*) argument passed with this file-control should be a pointer to a
** value of type (int). The integer value is set to 1 if the database is a wal
** mode database and there exists at least one client in another process that
** currently has an SQL transaction open on the database. It is set to 0 if
** the database is not a wal-mode db, or if there is no such connection in any
** other process. This opcode cannot be used to detect transactions opened
** by clients within the current process, only within other processes.
** </ul>
**
** <li>[[SQLITE_FCNTL_CKSM_FILE]]
** Used by the cksmvfs VFS module only.
** </ul>
*/
#define SQLITE_FCNTL_LOCKSTATE 1
#define SQLITE_FCNTL_GET_LOCKPROXYFILE 2
@ -1167,6 +1222,8 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_CKPT_DONE 37
#define SQLITE_FCNTL_RESERVE_BYTES 38
#define SQLITE_FCNTL_CKPT_START 39
#define SQLITE_FCNTL_EXTERNAL_READER 40
#define SQLITE_FCNTL_CKSM_FILE 41
/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
@ -2445,11 +2502,14 @@ SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3*,sqlite3_int64);
** CAPI3REF: Count The Number Of Rows Modified
** METHOD: sqlite3
**
** ^This function returns the number of rows modified, inserted or
** ^These functions return the number of rows modified, inserted or
** deleted by the most recently completed INSERT, UPDATE or DELETE
** statement on the database connection specified by the only parameter.
** ^Executing any other type of SQL statement does not modify the value
** returned by this function.
** The two functions are identical except for the type of the return value
** and that if the number of rows modified by the most recent INSERT, UPDATE
** or DELETE is greater than the maximum value supported by type "int", then
** the return value of sqlite3_changes() is undefined. ^Executing any other
** type of SQL statement does not modify the value returned by these functions.
**
** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are
** considered - auxiliary changes caused by [CREATE TRIGGER | triggers],
@ -2498,16 +2558,21 @@ SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3*,sqlite3_int64);
** </ul>
*/
SQLITE_API int sqlite3_changes(sqlite3*);
SQLITE_API sqlite3_int64 sqlite3_changes64(sqlite3*);
/*
** CAPI3REF: Total Number Of Rows Modified
** METHOD: sqlite3
**
** ^This function returns the total number of rows inserted, modified or
** ^These functions return the total number of rows inserted, modified or
** deleted by all [INSERT], [UPDATE] or [DELETE] statements completed
** since the database connection was opened, including those executed as
** part of trigger programs. ^Executing any other type of SQL statement
** does not affect the value returned by sqlite3_total_changes().
** part of trigger programs. The two functions are identical except for the
** type of the return value and that if the number of rows modified by the
** connection exceeds the maximum value supported by type "int", then
** the return value of sqlite3_total_changes() is undefined. ^Executing
** any other type of SQL statement does not affect the value returned by
** sqlite3_total_changes().
**
** ^Changes made as part of [foreign key actions] are included in the
** count, but those made as part of REPLACE constraint resolution are
@ -2535,6 +2600,7 @@ SQLITE_API int sqlite3_changes(sqlite3*);
** </ul>
*/
SQLITE_API int sqlite3_total_changes(sqlite3*);
SQLITE_API sqlite3_int64 sqlite3_total_changes64(sqlite3*);
/*
** CAPI3REF: Interrupt A Long-Running Query
@ -3364,6 +3430,14 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** the default shared cache setting provided by
** [sqlite3_enable_shared_cache()].)^
**
** [[OPEN_EXRESCODE]] ^(<dt>[SQLITE_OPEN_EXRESCODE]</dt>
** <dd>The database connection comes up in "extended result code mode".
** In other words, the database behaves has if
** [sqlite3_extended_result_codes(db,1)] where called on the database
** connection as soon as the connection is created. In addition to setting
** the extended result code mode, this flag also causes [sqlite3_open_v2()]
** to return an extended result code.</dd>
**
** [[OPEN_NOFOLLOW]] ^(<dt>[SQLITE_OPEN_NOFOLLOW]</dt>
** <dd>The database filename is not allowed to be a symbolic link</dd>
** </dl>)^
@ -3371,7 +3445,15 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** If the 3rd parameter to sqlite3_open_v2() is not one of the
** required combinations shown above optionally combined with other
** [SQLITE_OPEN_READONLY | SQLITE_OPEN_* bits]
** then the behavior is undefined.
** then the behavior is undefined. Historic versions of SQLite
** have silently ignored surplus bits in the flags parameter to
** sqlite3_open_v2(), however that behavior might not be carried through
** into future versions of SQLite and so applications should not rely
** upon it. Note in particular that the SQLITE_OPEN_EXCLUSIVE flag is a no-op
** for sqlite3_open_v2(). The SQLITE_OPEN_EXCLUSIVE does *not* cause
** the open to fail if the database already exists. The SQLITE_OPEN_EXCLUSIVE
** flag is intended for use by the [sqlite3_vfs|VFS interface] only, and not
** by sqlite3_open_v2().
**
** ^The fourth parameter to sqlite3_open_v2() is the name of the
** [sqlite3_vfs] object that defines the operating system interface that
@ -3742,13 +3824,14 @@ SQLITE_API void sqlite3_free_filename(char*);
** sqlite3_extended_errcode() might change with each API call.
** Except, there are some interfaces that are guaranteed to never
** change the value of the error code. The error-code preserving
** interfaces are:
** interfaces include the following:
**
** <ul>
** <li> sqlite3_errcode()
** <li> sqlite3_extended_errcode()
** <li> sqlite3_errmsg()
** <li> sqlite3_errmsg16()
** <li> sqlite3_error_offset()
** </ul>
**
** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language
@ -3763,6 +3846,13 @@ SQLITE_API void sqlite3_free_filename(char*);
** ^(Memory to hold the error message string is managed internally
** and must not be freed by the application)^.
**
** ^If the most recent error references a specific token in the input
** SQL, the sqlite3_error_offset() interface returns the byte offset
** of the start of that token. ^The byte offset returned by
** sqlite3_error_offset() assumes that the input SQL is UTF8.
** ^If the most recent error does not reference a specific token in the input
** SQL, then the sqlite3_error_offset() function returns -1.
**
** When the serialized [threading mode] is in use, it might be the
** case that a second error occurs on a separate thread in between
** the time of the first error and the call to these interfaces.
@ -3782,6 +3872,7 @@ 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 sqlite3_error_offset(sqlite3 *db);
/*
** CAPI3REF: Prepared Statement Object
@ -4139,12 +4230,17 @@ SQLITE_API int sqlite3_prepare16_v3(
** are managed by SQLite and are automatically freed when the prepared
** statement is finalized.
** ^The string returned by sqlite3_expanded_sql(P), on the other hand,
** is obtained from [sqlite3_malloc()] and must be free by the application
** is obtained from [sqlite3_malloc()] and must be freed by the application
** by passing it to [sqlite3_free()].
**
** ^The sqlite3_normalized_sql() interface is only available if
** the [SQLITE_ENABLE_NORMALIZE] compile-time option is defined.
*/
SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt);
SQLITE_API char *sqlite3_expanded_sql(sqlite3_stmt *pStmt);
#ifdef SQLITE_ENABLE_NORMALIZE
SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt);
#endif
/*
** CAPI3REF: Determine If An SQL Statement Writes The Database
@ -4179,6 +4275,19 @@ SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt);
** [BEGIN] merely sets internal flags, but the [BEGIN|BEGIN IMMEDIATE] and
** [BEGIN|BEGIN EXCLUSIVE] commands do touch the database and so
** sqlite3_stmt_readonly() returns false for those commands.
**
** ^This routine returns false if there is any possibility that the
** statement might change the database file. ^A false return does
** not guarantee that the statement will change the database file.
** ^For example, an UPDATE statement might have a WHERE clause that
** makes it a no-op, but the sqlite3_stmt_readonly() result would still
** be false. ^Similarly, a CREATE TABLE IF NOT EXISTS statement is a
** read-only no-op if the table already exists, but
** sqlite3_stmt_readonly() still returns false for such a statement.
**
** ^If prepared statement X is an [EXPLAIN] or [EXPLAIN QUERY PLAN]
** statement, then sqlite3_stmt_readonly(X) returns the same value as
** if the EXPLAIN or EXPLAIN QUERY PLAN prefix were omitted.
*/
SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
@ -4247,6 +4356,8 @@ SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*);
**
** ^The sqlite3_value objects that are passed as parameters into the
** implementation of [application-defined SQL functions] are protected.
** ^The sqlite3_value objects returned by [sqlite3_vtab_rhs_value()]
** are protected.
** ^The sqlite3_value object returned by
** [sqlite3_column_value()] is unprotected.
** Unprotected sqlite3_value objects may only be used as arguments
@ -4348,18 +4459,22 @@ typedef struct sqlite3_context sqlite3_context;
** contain embedded NULs. The result of expressions involving strings
** with embedded NULs is undefined.
**
** ^The fifth argument to the BLOB and string binding interfaces
** is a destructor used to dispose of the BLOB or
** string after SQLite has finished with it. ^The destructor is called
** to dispose of the BLOB or string even if the call to the bind API fails,
** except the destructor is not called if the third parameter is a NULL
** pointer or the fourth parameter is negative.
** ^If the fifth argument is
** the special value [SQLITE_STATIC], then SQLite assumes that the
** information is in static, unmanaged space and does not need to be freed.
** ^If the fifth argument has the value [SQLITE_TRANSIENT], then
** SQLite makes its own private copy of the data immediately, before
** the sqlite3_bind_*() routine returns.
** ^The fifth argument to the BLOB and string binding interfaces controls
** or indicates the lifetime of the object referenced by the third parameter.
** These three options exist:
** ^ (1) A destructor to dispose of the BLOB or string after SQLite has finished
** with it may be passed. ^It is called to dispose of the BLOB or string even
** if the call to the bind API fails, except the destructor is not called if
** the third parameter is a NULL pointer or the fourth parameter is negative.
** ^ (2) The special constant, [SQLITE_STATIC], may be passsed to indicate that
** the application remains responsible for disposing of the object. ^In this
** case, the object and the provided pointer to it must remain valid until
** either the prepared statement is finalized or the same SQL parameter is
** bound to something else, whichever occurs sooner.
** ^ (3) The constant, [SQLITE_TRANSIENT], may be passed to indicate that the
** object is to be copied prior to the return from sqlite3_bind_*(). ^The
** object and pointer to it must remain valid until then. ^SQLite will then
** manage the lifetime of its private copy.
**
** ^The sixth argument to sqlite3_bind_text64() must be one of
** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE]
@ -4864,6 +4979,10 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
** even empty strings, are always zero-terminated. ^The return
** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer.
**
** ^Strings returned by sqlite3_column_text16() always have the endianness
** which is native to the platform, regardless of the text encoding set
** for the database.
**
** <b>Warning:</b> ^The object returned by [sqlite3_column_value()] is an
** [unprotected sqlite3_value] object. In a multithreaded environment,
** an unprotected sqlite3_value object may only be used safely with
@ -4877,7 +4996,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
** [application-defined SQL functions] or [virtual tables], not within
** top-level application code.
**
** The these routines may attempt to convert the datatype of the result.
** These routines may attempt to convert the datatype of the result.
** ^For example, if the internal representation is FLOAT and a text result
** is requested, [sqlite3_snprintf()] is used internally to perform the
** conversion automatically. ^(The following table details the conversions
@ -4902,7 +5021,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
** <tr><td> TEXT <td> BLOB <td> No change
** <tr><td> BLOB <td> INTEGER <td> [CAST] to INTEGER
** <tr><td> BLOB <td> FLOAT <td> [CAST] to REAL
** <tr><td> BLOB <td> TEXT <td> Add a zero terminator if needed
** <tr><td> BLOB <td> TEXT <td> [CAST] to TEXT, ensure zero terminator
** </table>
** </blockquote>)^
**
@ -5101,7 +5220,6 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
** within VIEWs, TRIGGERs, CHECK constraints, generated column expressions,
** index expressions, or the WHERE clause of partial indexes.
**
** <span style="background-color:#ffff90;">
** For best security, the [SQLITE_DIRECTONLY] flag is recommended for
** all application-defined SQL functions that do not need to be
** used inside of triggers, view, CHECK constraints, or other elements of
@ -5111,7 +5229,6 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
** a database file to include invocations of the function with parameters
** chosen by the attacker, which the application will then execute when
** the database file is opened and read.
** </span>
**
** ^(The fifth parameter is an arbitrary pointer. The implementation of the
** function can gain access to this pointer using [sqlite3_user_data()].)^
@ -5476,7 +5593,8 @@ SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*);
** object D and returns a pointer to that copy. ^The [sqlite3_value] returned
** is a [protected sqlite3_value] object even if the input is not.
** ^The sqlite3_value_dup(V) interface returns NULL if V is NULL or if a
** memory allocation fails.
** memory allocation fails. ^If V is a [pointer value], then the result
** of sqlite3_value_dup(V) is a NULL value.
**
** ^The sqlite3_value_free(V) interface frees an [sqlite3_value] object
** previously obtained from [sqlite3_value_dup()]. ^If V is a NULL pointer
@ -6158,6 +6276,28 @@ SQLITE_API int sqlite3_get_autocommit(sqlite3*);
*/
SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
/*
** CAPI3REF: Return The Schema Name For A Database Connection
** METHOD: sqlite3
**
** ^The sqlite3_db_name(D,N) interface returns a pointer to the schema name
** for the N-th database on database connection D, or a NULL pointer of N is
** out of range. An N value of 0 means the main database file. An N of 1 is
** the "temp" schema. Larger values of N correspond to various ATTACH-ed
** databases.
**
** Space to hold the string that is returned by sqlite3_db_name() is managed
** by SQLite itself. The string might be deallocated by any operation that
** changes the schema, including [ATTACH] or [DETACH] or calls to
** [sqlite3_serialize()] or [sqlite3_deserialize()], even operations that
** occur on a different thread. Applications that need to
** remember the string long-term should make their own copy. Applications that
** are accessing the same database connection simultaneously on multiple
** threads should mutex-protect calls to this API and should make their own
** private copy of the result prior to releasing the mutex.
*/
SQLITE_API const char *sqlite3_db_name(sqlite3 *db, int N);
/*
** CAPI3REF: Return The Filename For A Database Connection
** METHOD: sqlite3
@ -6317,6 +6457,72 @@ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt);
SQLITE_API void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*);
SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
/*
** CAPI3REF: Autovacuum Compaction Amount Callback
** METHOD: sqlite3
**
** ^The sqlite3_autovacuum_pages(D,C,P,X) interface registers a callback
** function C that is invoked prior to each autovacuum of the database
** file. ^The callback is passed a copy of the generic data pointer (P),
** the schema-name of the attached database that is being autovacuumed,
** the the size of the database file in pages, the number of free pages,
** and the number of bytes per page, respectively. The callback should
** return the number of free pages that should be removed by the
** autovacuum. ^If the callback returns zero, then no autovacuum happens.
** ^If the value returned is greater than or equal to the number of
** free pages, then a complete autovacuum happens.
**
** <p>^If there are multiple ATTACH-ed database files that are being
** modified as part of a transaction commit, then the autovacuum pages
** callback is invoked separately for each file.
**
** <p><b>The callback is not reentrant.</b> The callback function should
** not attempt to invoke any other SQLite interface. If it does, bad
** things may happen, including segmentation faults and corrupt database
** files. The callback function should be a simple function that
** does some arithmetic on its input parameters and returns a result.
**
** ^The X parameter to sqlite3_autovacuum_pages(D,C,P,X) is an optional
** destructor for the P parameter. ^If X is not NULL, then X(P) is
** invoked whenever the database connection closes or when the callback
** is overwritten by another invocation of sqlite3_autovacuum_pages().
**
** <p>^There is only one autovacuum pages callback per database connection.
** ^Each call to the sqlite3_autovacuum_pages() interface overrides all
** previous invocations for that database connection. ^If the callback
** argument (C) to sqlite3_autovacuum_pages(D,C,P,X) is a NULL pointer,
** then the autovacuum steps callback is cancelled. The return value
** from sqlite3_autovacuum_pages() is normally SQLITE_OK, but might
** be some other error code if something goes wrong. The current
** implementation will only return SQLITE_OK or SQLITE_MISUSE, but other
** return codes might be added in future releases.
**
** <p>If no autovacuum pages callback is specified (the usual case) or
** a NULL pointer is provided for the callback,
** then the default behavior is to vacuum all free pages. So, in other
** words, the default behavior is the same as if the callback function
** were something like this:
**
** <blockquote><pre>
** &nbsp; unsigned int demonstration_autovac_pages_callback(
** &nbsp; void *pClientData,
** &nbsp; const char *zSchema,
** &nbsp; unsigned int nDbPage,
** &nbsp; unsigned int nFreePage,
** &nbsp; unsigned int nBytePerPage
** &nbsp; ){
** &nbsp; return nFreePage;
** &nbsp; }
** </pre></blockquote>
*/
SQLITE_API int sqlite3_autovacuum_pages(
sqlite3 *db,
unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int),
void*,
void(*)(void*)
);
/*
** CAPI3REF: Data Change Notification Callbacks
** METHOD: sqlite3
@ -6958,24 +7164,56 @@ struct sqlite3_index_info {
**
** These macros define the allowed values for the
** [sqlite3_index_info].aConstraint[].op field. Each value represents
** an operator that is part of a constraint term in the wHERE clause of
** an operator that is part of a constraint term in the WHERE clause of
** a query that uses a [virtual table].
**
** ^The left-hand operand of the operator is given by the corresponding
** aConstraint[].iColumn field. ^An iColumn of -1 indicates the left-hand
** operand is the rowid.
** The SQLITE_INDEX_CONSTRAINT_LIMIT and SQLITE_INDEX_CONSTRAINT_OFFSET
** operators have no left-hand operand, and so for those operators the
** corresponding aConstraint[].iColumn is meaningless and should not be
** used.
**
** All operator values from SQLITE_INDEX_CONSTRAINT_FUNCTION through
** value 255 are reserved to represent functions that are overloaded
** by the [xFindFunction|xFindFunction method] of the virtual table
** implementation.
**
** The right-hand operands for each constraint might be accessible using
** the [sqlite3_vtab_rhs_value()] interface. Usually the right-hand
** operand is only available if it appears as a single constant literal
** in the input SQL. If the right-hand operand is another column or an
** expression (even a constant expression) or a parameter, then the
** sqlite3_vtab_rhs_value() probably will not be able to extract it.
** ^The SQLITE_INDEX_CONSTRAINT_ISNULL and
** SQLITE_INDEX_CONSTRAINT_ISNOTNULL operators have no right-hand operand
** and hence calls to sqlite3_vtab_rhs_value() for those operators will
** always return SQLITE_NOTFOUND.
**
** The collating sequence to be used for comparison can be found using
** the [sqlite3_vtab_collation()] interface. For most real-world virtual
** tables, the collating sequence of constraints does not matter (for example
** because the constraints are numeric) and so the sqlite3_vtab_collation()
** interface is no commonly needed.
*/
#define SQLITE_INDEX_CONSTRAINT_EQ 2
#define SQLITE_INDEX_CONSTRAINT_GT 4
#define SQLITE_INDEX_CONSTRAINT_LE 8
#define SQLITE_INDEX_CONSTRAINT_LT 16
#define SQLITE_INDEX_CONSTRAINT_GE 32
#define SQLITE_INDEX_CONSTRAINT_MATCH 64
#define SQLITE_INDEX_CONSTRAINT_LIKE 65
#define SQLITE_INDEX_CONSTRAINT_GLOB 66
#define SQLITE_INDEX_CONSTRAINT_REGEXP 67
#define SQLITE_INDEX_CONSTRAINT_NE 68
#define SQLITE_INDEX_CONSTRAINT_ISNOT 69
#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70
#define SQLITE_INDEX_CONSTRAINT_ISNULL 71
#define SQLITE_INDEX_CONSTRAINT_IS 72
#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150
#define SQLITE_INDEX_CONSTRAINT_EQ 2
#define SQLITE_INDEX_CONSTRAINT_GT 4
#define SQLITE_INDEX_CONSTRAINT_LE 8
#define SQLITE_INDEX_CONSTRAINT_LT 16
#define SQLITE_INDEX_CONSTRAINT_GE 32
#define SQLITE_INDEX_CONSTRAINT_MATCH 64
#define SQLITE_INDEX_CONSTRAINT_LIKE 65
#define SQLITE_INDEX_CONSTRAINT_GLOB 66
#define SQLITE_INDEX_CONSTRAINT_REGEXP 67
#define SQLITE_INDEX_CONSTRAINT_NE 68
#define SQLITE_INDEX_CONSTRAINT_ISNOT 69
#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70
#define SQLITE_INDEX_CONSTRAINT_ISNULL 71
#define SQLITE_INDEX_CONSTRAINT_IS 72
#define SQLITE_INDEX_CONSTRAINT_LIMIT 73
#define SQLITE_INDEX_CONSTRAINT_OFFSET 74
#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150
/*
** CAPI3REF: Register A Virtual Table Implementation
@ -7004,7 +7242,7 @@ struct sqlite3_index_info {
** destructor.
**
** ^If the third parameter (the pointer to the sqlite3_module object) is
** NULL then no new module is create and any existing modules with the
** NULL then no new module is created and any existing modules with the
** same name are dropped.
**
** See also: [sqlite3_drop_modules()]
@ -7779,7 +8017,9 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29
#define SQLITE_TESTCTRL_SEEK_COUNT 30
#define SQLITE_TESTCTRL_TRACEFLAGS 31
#define SQLITE_TESTCTRL_LAST 31 /* Largest TESTCTRL */
#define SQLITE_TESTCTRL_TUNE 32
#define SQLITE_TESTCTRL_LOGEST 33
#define SQLITE_TESTCTRL_LAST 33 /* Largest TESTCTRL */
/*
** CAPI3REF: SQL Keyword Checking
@ -8302,6 +8542,16 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
** The counter is incremented on the first [sqlite3_step()] call of each
** cycle.
**
** [[SQLITE_STMTSTATUS_FILTER_MISS]]
** [[SQLITE_STMTSTATUS_FILTER HIT]]
** <dt>SQLITE_STMTSTATUS_FILTER_HIT<br>
** SQLITE_STMTSTATUS_FILTER_MISS</dt>
** <dd>^SQLITE_STMTSTATUS_FILTER_HIT is the number of times that a join
** step was bypassed because a Bloom filter returned not-found. The
** corresponding SQLITE_STMTSTATUS_FILTER_MISS value is the number of
** times that the Bloom filter returned a find, and thus the join step
** had to be processed as normal.
**
** [[SQLITE_STMTSTATUS_MEMUSED]] <dt>SQLITE_STMTSTATUS_MEMUSED</dt>
** <dd>^This is the approximate number of bytes of heap memory
** used to store the prepared statement. ^This value is not actually
@ -8316,6 +8566,8 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
#define SQLITE_STMTSTATUS_VM_STEP 4
#define SQLITE_STMTSTATUS_REPREPARE 5
#define SQLITE_STMTSTATUS_RUN 6
#define SQLITE_STMTSTATUS_FILTER_MISS 7
#define SQLITE_STMTSTATUS_FILTER_HIT 8
#define SQLITE_STMTSTATUS_MEMUSED 99
/*
@ -8979,8 +9231,9 @@ SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...);
**
** A single database handle may have at most a single write-ahead log callback
** registered at one time. ^Calling [sqlite3_wal_hook()] replaces any
** previously registered write-ahead log callback. ^Note that the
** [sqlite3_wal_autocheckpoint()] interface and the
** previously registered write-ahead log callback. ^The return value is
** a copy of the third parameter from the previous call, if any, or 0.
** ^Note that the [sqlite3_wal_autocheckpoint()] interface and the
** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will
** overwrite any prior [sqlite3_wal_hook()] settings.
*/
@ -9283,19 +9536,276 @@ SQLITE_API int sqlite3_vtab_nochange(sqlite3_context*);
/*
** CAPI3REF: Determine The Collation For a Virtual Table Constraint
** METHOD: sqlite3_index_info
**
** This function may only be called from within a call to the [xBestIndex]
** method of a [virtual table].
** method of a [virtual table]. This function returns a pointer to a string
** that is the name of the appropriate collation sequence to use for text
** comparisons on the constraint identified by its arguments.
**
** The first argument must be the sqlite3_index_info object that is the
** first parameter to the xBestIndex() method. The second argument must be
** an index into the aConstraint[] array belonging to the sqlite3_index_info
** structure passed to xBestIndex. This function returns a pointer to a buffer
** containing the name of the collation sequence for the corresponding
** constraint.
** The first argument must be the pointer to the [sqlite3_index_info] object
** that is the first parameter to the xBestIndex() method. The second argument
** must be an index into the aConstraint[] array belonging to the
** sqlite3_index_info structure passed to xBestIndex.
**
** Important:
** The first parameter must be the same pointer that is passed into the
** xBestMethod() method. The first parameter may not be a pointer to a
** different [sqlite3_index_info] object, even an exact copy.
**
** The return value is computed as follows:
**
** <ol>
** <li><p> If the constraint comes from a WHERE clause expression that contains
** a [COLLATE operator], then the name of the collation specified by
** that COLLATE operator is returned.
** <li><p> If there is no COLLATE operator, but the column that is the subject
** of the constraint specifies an alternative collating sequence via
** a [COLLATE clause] on the column definition within the CREATE TABLE
** statement that was passed into [sqlite3_declare_vtab()], then the
** name of that alternative collating sequence is returned.
** <li><p> Otherwise, "BINARY" is returned.
** </ol>
*/
SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int);
/*
** CAPI3REF: Determine if a virtual table query is DISTINCT
** METHOD: sqlite3_index_info
**
** This API may only be used from within an [xBestIndex|xBestIndex method]
** of a [virtual table] implementation. The result of calling this
** interface from outside of xBestIndex() is undefined and probably harmful.
**
** ^The sqlite3_vtab_distinct() interface returns an integer between 0 and
** 3. The integer returned by sqlite3_vtab_distinct()
** gives the virtual table additional information about how the query
** planner wants the output to be ordered. As long as the virtual table
** can meet the ordering requirements of the query planner, it may set
** the "orderByConsumed" flag.
**
** <ol><li value="0"><p>
** ^If the sqlite3_vtab_distinct() interface returns 0, that means
** that the query planner needs the virtual table to return all rows in the
** sort order defined by the "nOrderBy" and "aOrderBy" fields of the
** [sqlite3_index_info] object. This is the default expectation. If the
** virtual table outputs all rows in sorted order, then it is always safe for
** the xBestIndex method to set the "orderByConsumed" flag, regardless of
** the return value from sqlite3_vtab_distinct().
** <li value="1"><p>
** ^(If the sqlite3_vtab_distinct() interface returns 1, that means
** that the query planner does not need the rows to be returned in sorted order
** as long as all rows with the same values in all columns identified by the
** "aOrderBy" field are adjacent.)^ This mode is used when the query planner
** is doing a GROUP BY.
** <li value="2"><p>
** ^(If the sqlite3_vtab_distinct() interface returns 2, that means
** that the query planner does not need the rows returned in any particular
** order, as long as rows with the same values in all "aOrderBy" columns
** are adjacent.)^ ^(Furthermore, only a single row for each particular
** combination of values in the columns identified by the "aOrderBy" field
** needs to be returned.)^ ^It is always ok for two or more rows with the same
** values in all "aOrderBy" columns to be returned, as long as all such rows
** are adjacent. ^The virtual table may, if it chooses, omit extra rows
** that have the same value for all columns identified by "aOrderBy".
** ^However omitting the extra rows is optional.
** This mode is used for a DISTINCT query.
** <li value="3"><p>
** ^(If the sqlite3_vtab_distinct() interface returns 3, that means
** that the query planner needs only distinct rows but it does need the
** rows to be sorted.)^ ^The virtual table implementation is free to omit
** rows that are identical in all aOrderBy columns, if it wants to, but
** it is not required to omit any rows. This mode is used for queries
** that have both DISTINCT and ORDER BY clauses.
** </ol>
**
** ^For the purposes of comparing virtual table output values to see if the
** values are same value for sorting purposes, two NULL values are considered
** to be the same. In other words, the comparison operator is "IS"
** (or "IS NOT DISTINCT FROM") and not "==".
**
** If a virtual table implementation is unable to meet the requirements
** specified above, then it must not set the "orderByConsumed" flag in the
** [sqlite3_index_info] object or an incorrect answer may result.
**
** ^A virtual table implementation is always free to return rows in any order
** it wants, as long as the "orderByConsumed" flag is not set. ^When the
** the "orderByConsumed" flag is unset, the query planner will add extra
** [bytecode] to ensure that the final results returned by the SQL query are
** ordered correctly. The use of the "orderByConsumed" flag and the
** sqlite3_vtab_distinct() interface is merely an optimization. ^Careful
** use of the sqlite3_vtab_distinct() interface and the "orderByConsumed"
** flag might help queries against a virtual table to run faster. Being
** overly aggressive and setting the "orderByConsumed" flag when it is not
** valid to do so, on the other hand, might cause SQLite to return incorrect
** results.
*/
SQLITE_API int sqlite3_vtab_distinct(sqlite3_index_info*);
/*
** CAPI3REF: Identify and handle IN constraints in xBestIndex
**
** This interface may only be used from within an
** [xBestIndex|xBestIndex() method] of a [virtual table] implementation.
** The result of invoking this interface from any other context is
** undefined and probably harmful.
**
** ^(A constraint on a virtual table of the form
** "[IN operator|column IN (...)]" is
** communicated to the xBestIndex method as a
** [SQLITE_INDEX_CONSTRAINT_EQ] constraint.)^ If xBestIndex wants to use
** this constraint, it must set the corresponding
** aConstraintUsage[].argvIndex to a postive integer. ^(Then, under
** the usual mode of handling IN operators, SQLite generates [bytecode]
** that invokes the [xFilter|xFilter() method] once for each value
** on the right-hand side of the IN operator.)^ Thus the virtual table
** only sees a single value from the right-hand side of the IN operator
** at a time.
**
** In some cases, however, it would be advantageous for the virtual
** table to see all values on the right-hand of the IN operator all at
** once. The sqlite3_vtab_in() interfaces facilitates this in two ways:
**
** <ol>
** <li><p>
** ^A call to sqlite3_vtab_in(P,N,-1) will return true (non-zero)
** if and only if the [sqlite3_index_info|P->aConstraint][N] constraint
** is an [IN operator] that can be processed all at once. ^In other words,
** sqlite3_vtab_in() with -1 in the third argument is a mechanism
** by which the virtual table can ask SQLite if all-at-once processing
** of the IN operator is even possible.
**
** <li><p>
** ^A call to sqlite3_vtab_in(P,N,F) with F==1 or F==0 indicates
** to SQLite that the virtual table does or does not want to process
** the IN operator all-at-once, respectively. ^Thus when the third
** parameter (F) is non-negative, this interface is the mechanism by
** which the virtual table tells SQLite how it wants to process the
** IN operator.
** </ol>
**
** ^The sqlite3_vtab_in(P,N,F) interface can be invoked multiple times
** within the same xBestIndex method call. ^For any given P,N pair,
** the return value from sqlite3_vtab_in(P,N,F) will always be the same
** within the same xBestIndex call. ^If the interface returns true
** (non-zero), that means that the constraint is an IN operator
** that can be processed all-at-once. ^If the constraint is not an IN
** operator or cannot be processed all-at-once, then the interface returns
** false.
**
** ^(All-at-once processing of the IN operator is selected if both of the
** following conditions are met:
**
** <ol>
** <li><p> The P->aConstraintUsage[N].argvIndex value is set to a positive
** integer. This is how the virtual table tells SQLite that it wants to
** use the N-th constraint.
**
** <li><p> The last call to sqlite3_vtab_in(P,N,F) for which F was
** non-negative had F>=1.
** </ol>)^
**
** ^If either or both of the conditions above are false, then SQLite uses
** the traditional one-at-a-time processing strategy for the IN constraint.
** ^If both conditions are true, then the argvIndex-th parameter to the
** xFilter method will be an [sqlite3_value] that appears to be NULL,
** but which can be passed to [sqlite3_vtab_in_first()] and
** [sqlite3_vtab_in_next()] to find all values on the right-hand side
** of the IN constraint.
*/
SQLITE_API int sqlite3_vtab_in(sqlite3_index_info*, int iCons, int bHandle);
/*
** CAPI3REF: Find all elements on the right-hand side of an IN constraint.
**
** These interfaces are only useful from within the
** [xFilter|xFilter() method] of a [virtual table] implementation.
** The result of invoking these interfaces from any other context
** is undefined and probably harmful.
**
** The X parameter in a call to sqlite3_vtab_in_first(X,P) or
** sqlite3_vtab_in_next(X,P) must be one of the parameters to the
** xFilter method which invokes these routines, and specifically
** a parameter that was previously selected for all-at-once IN constraint
** processing use the [sqlite3_vtab_in()] interface in the
** [xBestIndex|xBestIndex method]. ^(If the X parameter is not
** an xFilter argument that was selected for all-at-once IN constraint
** processing, then these routines return [SQLITE_MISUSE])^ or perhaps
** exhibit some other undefined or harmful behavior.
**
** ^(Use these routines to access all values on the right-hand side
** of the IN constraint using code like the following:
**
** <blockquote><pre>
** &nbsp; for(rc=sqlite3_vtab_in_first(pList, &pVal);
** &nbsp; rc==SQLITE_OK && pVal
** &nbsp; rc=sqlite3_vtab_in_next(pList, &pVal)
** &nbsp; ){
** &nbsp; // do something with pVal
** &nbsp; }
** &nbsp; if( rc!=SQLITE_OK ){
** &nbsp; // an error has occurred
** &nbsp; }
** </pre></blockquote>)^
**
** ^On success, the sqlite3_vtab_in_first(X,P) and sqlite3_vtab_in_next(X,P)
** routines return SQLITE_OK and set *P to point to the first or next value
** on the RHS of the IN constraint. ^If there are no more values on the
** right hand side of the IN constraint, then *P is set to NULL and these
** routines return [SQLITE_DONE]. ^The return value might be
** some other value, such as SQLITE_NOMEM, in the event of a malfunction.
**
** The *ppOut values returned by these routines are only valid until the
** next call to either of these routines or until the end of the xFilter
** method from which these routines were called. If the virtual table
** implementation needs to retain the *ppOut values for longer, it must make
** copies. The *ppOut values are [protected sqlite3_value|protected].
*/
SQLITE_API int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut);
SQLITE_API int sqlite3_vtab_in_next(sqlite3_value *pVal, sqlite3_value **ppOut);
/*
** CAPI3REF: Constraint values in xBestIndex()
** METHOD: sqlite3_index_info
**
** This API may only be used from within the [xBestIndex|xBestIndex method]
** of a [virtual table] implementation. The result of calling this interface
** from outside of an xBestIndex method are undefined and probably harmful.
**
** ^When the sqlite3_vtab_rhs_value(P,J,V) interface is invoked from within
** the [xBestIndex] method of a [virtual table] implementation, with P being
** a copy of the [sqlite3_index_info] object pointer passed into xBestIndex and
** J being a 0-based index into P->aConstraint[], then this routine
** attempts to set *V to the value of the right-hand operand of
** that constraint if the right-hand operand is known. ^If the
** right-hand operand is not known, then *V is set to a NULL pointer.
** ^The sqlite3_vtab_rhs_value(P,J,V) interface returns SQLITE_OK if
** and only if *V is set to a value. ^The sqlite3_vtab_rhs_value(P,J,V)
** inteface returns SQLITE_NOTFOUND if the right-hand side of the J-th
** constraint is not available. ^The sqlite3_vtab_rhs_value() interface
** can return an result code other than SQLITE_OK or SQLITE_NOTFOUND if
** something goes wrong.
**
** The sqlite3_vtab_rhs_value() interface is usually only successful if
** the right-hand operand of a constraint is a literal value in the original
** SQL statement. If the right-hand operand is an expression or a reference
** to some other column or a [host parameter], then sqlite3_vtab_rhs_value()
** will probably return [SQLITE_NOTFOUND].
**
** ^(Some constraints, such as [SQLITE_INDEX_CONSTRAINT_ISNULL] and
** [SQLITE_INDEX_CONSTRAINT_ISNOTNULL], have no right-hand operand. For such
** constraints, sqlite3_vtab_rhs_value() always returns SQLITE_NOTFOUND.)^
**
** ^The [sqlite3_value] object returned in *V is a protected sqlite3_value
** and remains valid for the duration of the xBestIndex method call.
** ^When xBestIndex returns, the sqlite3_value object returned by
** sqlite3_vtab_rhs_value() is automatically deallocated.
**
** The "_rhs_" in the name of this routine is an abbreviation for
** "Right-Hand Side".
*/
SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **ppVal);
/*
** CAPI3REF: Conflict resolution modes
** KEYWORDS: {conflict resolution mode}
@ -9531,6 +10041,15 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*);
** triggers; or 2 for changes resulting from triggers called by top-level
** triggers; and so forth.
**
** When the [sqlite3_blob_write()] API is used to update a blob column,
** the pre-update hook is invoked with SQLITE_DELETE. This is because the
** in this case the new values are not available. In this case, when a
** callback made with op==SQLITE_DELETE is actuall a write using the
** sqlite3_blob_write() API, the [sqlite3_preupdate_blobwrite()] returns
** the index of the column being written. In other cases, where the
** pre-update hook is being invoked for some other reason, including a
** regular DELETE, sqlite3_preupdate_blobwrite() returns -1.
**
** See also: [sqlite3_update_hook()]
*/
#if defined(SQLITE_ENABLE_PREUPDATE_HOOK)
@ -9551,6 +10070,7 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *, int, sqlite3_value **);
SQLITE_API int sqlite3_preupdate_count(sqlite3 *);
SQLITE_API int sqlite3_preupdate_depth(sqlite3 *);
SQLITE_API int sqlite3_preupdate_new(sqlite3 *, int, sqlite3_value **);
SQLITE_API int sqlite3_preupdate_blobwrite(sqlite3 *);
#endif
/*
@ -9789,8 +10309,8 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c
** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory
** allocation error occurs.
**
** This interface is only available if SQLite is compiled with the
** [SQLITE_ENABLE_DESERIALIZE] option.
** This interface is omitted if SQLite is compiled with the
** [SQLITE_OMIT_DESERIALIZE] option.
*/
SQLITE_API unsigned char *sqlite3_serialize(
sqlite3 *db, /* The database connection */
@ -9837,12 +10357,16 @@ SQLITE_API unsigned char *sqlite3_serialize(
** database is currently in a read transaction or is involved in a backup
** operation.
**
** It is not possible to deserialized into the TEMP database. If the
** S argument to sqlite3_deserialize(D,S,P,N,M,F) is "temp" then the
** function returns SQLITE_ERROR.
**
** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the
** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then
** [sqlite3_free()] is invoked on argument P prior to returning.
**
** This interface is only available if SQLite is compiled with the
** [SQLITE_ENABLE_DESERIALIZE] option.
** This interface is omitted if SQLite is compiled with the
** [SQLITE_OMIT_DESERIALIZE] option.
*/
SQLITE_API int sqlite3_deserialize(
sqlite3 *db, /* The database connection */
@ -10091,6 +10615,38 @@ SQLITE_API int sqlite3session_create(
*/
SQLITE_API void sqlite3session_delete(sqlite3_session *pSession);
/*
** CAPIREF: Conigure a Session Object
** METHOD: sqlite3_session
**
** This method is used to configure a session object after it has been
** created. At present the only valid value for the second parameter is
** [SQLITE_SESSION_OBJCONFIG_SIZE].
**
** Arguments for sqlite3session_object_config()
**
** The following values may passed as the the 4th parameter to
** sqlite3session_object_config().
**
** <dt>SQLITE_SESSION_OBJCONFIG_SIZE <dd>
** This option is used to set, clear or query the flag that enables
** the [sqlite3session_changeset_size()] API. Because it imposes some
** computational overhead, this API is disabled by default. Argument
** pArg must point to a value of type (int). If the value is initially
** 0, then the sqlite3session_changeset_size() API is disabled. If it
** is greater than 0, then the same API is enabled. Or, if the initial
** value is less than zero, no change is made. In all cases the (int)
** variable is set to 1 if the sqlite3session_changeset_size() API is
** enabled following the current call, or 0 otherwise.
**
** It is an error (SQLITE_MISUSE) to attempt to modify this setting after
** the first table has been attached to the session object.
*/
SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg);
/*
*/
#define SQLITE_SESSION_OBJCONFIG_SIZE 1
/*
** CAPI3REF: Enable Or Disable A Session Object
@ -10335,6 +10891,22 @@ SQLITE_API int sqlite3session_changeset(
void **ppChangeset /* OUT: Buffer containing changeset */
);
/*
** CAPI3REF: Return An Upper-limit For The Size Of The Changeset
** METHOD: sqlite3_session
**
** By default, this function always returns 0. For it to return
** a useful result, the sqlite3_session object must have been configured
** to enable this API using sqlite3session_object_config() with the
** SQLITE_SESSION_OBJCONFIG_SIZE verb.
**
** When enabled, this function returns an upper limit, in bytes, for the size
** of the changeset that might be produced if sqlite3session_changeset() were
** called. The final changeset size might be equal to or smaller than the
** size in bytes returned by this function.
*/
SQLITE_API sqlite3_int64 sqlite3session_changeset_size(sqlite3_session *pSession);
/*
** CAPI3REF: Load The Difference Between Tables Into A Session
** METHOD: sqlite3_session

View File

@ -358,6 +358,17 @@ TL_restrictionReason *TL_restrictionReason::TLdeserialize(NativeByteBuffer *stre
return result;
}
TL_username *TL_username::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
if (TL_username::constructor != constructor) {
error = true;
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_username", constructor);
return nullptr;
}
TL_username *result = new TL_username();
result->readParams(stream, instanceNum, error);
return result;
}
void TL_restrictionReason::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
platform = stream->readString(&error);
reason = stream->readString(&error);
@ -371,6 +382,21 @@ void TL_restrictionReason::serializeToStream(NativeByteBuffer *stream) {
stream->writeString(text);
}
void TL_username::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
flags = stream->readInt32(&error);
editable = (flags & 1) != 0;
active = (flags & 2) != 0;
username = stream->readString(&error);
}
void TL_username::serializeToStream(NativeByteBuffer *stream) {
stream->writeInt32(constructor);
flags = editable ? (flags | 1) : (flags &~ 1);
flags = active ? (flags | 2) : (flags &~ 2);
stream->writeInt32(flags);
stream->writeString(username);
}
User *User::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
User *result = nullptr;
switch (constructor) {
@ -400,6 +426,7 @@ void TL_userEmpty::serializeToStream(NativeByteBuffer *stream) {
void TL_user::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
flags = stream->readInt32(&error);
flags2 = stream->readInt32(&error);
id = stream->readInt64(&error);
if ((flags & 1) != 0) {
access_hash = stream->readInt64(&error);
@ -447,6 +474,22 @@ void TL_user::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &er
if ((flags & 4194304) != 0) {
lang_code = stream->readString(&error);
}
if ((flags2 & 1) != 0) {
uint32_t magic = stream->readUint32(&error);
if (magic != 0x1cb5c415) {
error = true;
if (LOGS_ENABLED) DEBUG_FATAL("wrong Vector magic, got %x", magic);
return;
}
int32_t count = stream->readInt32(&error);
for (int32_t a = 0; a < count; a++) {
TL_username *object = TL_username::TLdeserialize(stream, stream->readUint32(&error), instanceNum, error);
if (object == nullptr) {
return;
}
usernames.push_back(std::unique_ptr<TL_username>(object));
}
}
}
void TL_user::serializeToStream(NativeByteBuffer *stream) {

View File

@ -301,6 +301,20 @@ public:
void serializeToStream(NativeByteBuffer *stream);
};
class TL_username : public TLObject {
public:
static const uint32_t constructor = 0xb4073647;
int32_t flags;
bool editable;
bool active;
std::string username;
static TL_username *TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error);
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
void serializeToStream(NativeByteBuffer *stream);
};
class User : public TLObject {
public:
@ -313,10 +327,12 @@ public:
std::unique_ptr<UserProfilePhoto> photo;
std::unique_ptr<UserStatus> status;
int32_t flags;
int32_t flags2;
int32_t bot_info_version;
std::vector<std::unique_ptr<TL_restrictionReason>> restriction_reason;
std::string bot_inline_placeholder;
std::string lang_code;
std::vector<std::unique_ptr<TL_username>> usernames;
static User *TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error);
};
@ -333,7 +349,7 @@ public:
class TL_user : public User {
public:
static const uint32_t constructor = 0x5d99adee;
static const uint32_t constructor = 0x8f97c628;
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
void serializeToStream(NativeByteBuffer *stream);
@ -777,5 +793,4 @@ public:
void serializeToStream(NativeByteBuffer *stream);
};
#endif

View File

@ -1299,7 +1299,7 @@ void ConnectionsManager::processServerResponse(TLObject *message, int64_t messag
int32_t waitTime = 2;
static std::string floodWait = "FLOOD_WAIT_";
static std::string slowmodeWait = "SLOWMODE_WAIT_";
discardResponse = true;
discardResponse = (request->requestFlags & RequestFlagIgnoreFloodWait) == 0;
if (error->error_message.find(floodWait) != std::string::npos) {
std::string num = error->error_message.substr(floodWait.size(), error->error_message.size() - floodWait.size());
waitTime = atoi(num.c_str());

View File

@ -169,7 +169,8 @@ enum RequestFlag {
RequestFlagInvokeAfter = 64,
RequestFlagNeedQuickAck = 128,
RequestFlagUseUnboundKey = 256,
RequestFlagResendAfter = 512
RequestFlagResendAfter = 512,
RequestFlagIgnoreFloodWait = 1024
};
inline std::string to_string_int32(int32_t value) {

File diff suppressed because one or more lines are too long

View File

@ -4542,7 +4542,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView,
final int childCount = mChildHelper.getUnfilteredChildCount();
for (int i = 0; i < childCount; i++) {
final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
if (!holder.shouldIgnore()) {
if (holder != null && !holder.shouldIgnore()) {
holder.clearOldPosition();
}
}
@ -11645,7 +11645,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView,
*/
@Deprecated
public int getViewPosition() {
return mViewHolder.getPosition();
return mViewHolder == null ? RecyclerView.NO_POSITION : mViewHolder.getPosition();
}
/**
@ -11655,7 +11655,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView,
* @return the adapter position this view as of latest layout pass
*/
public int getViewLayoutPosition() {
return mViewHolder.getLayoutPosition();
return mViewHolder == null ? RecyclerView.NO_POSITION : mViewHolder.getLayoutPosition();
}
/**
@ -11667,7 +11667,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView,
* its up-to-date position cannot be calculated.
*/
public int getViewAdapterPosition() {
return mViewHolder.getAdapterPosition();
return mViewHolder == null ? RecyclerView.NO_POSITION :mViewHolder.getAdapterPosition();
}
}

View File

@ -10,8 +10,6 @@ package org.telegram.messenger;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.app.Activity;
@ -55,6 +53,8 @@ import android.provider.CallLog;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.provider.Settings;
import android.system.ErrnoException;
import android.system.OsConstants;
import android.telephony.TelephonyManager;
import android.text.Layout;
import android.text.Selection;
@ -66,6 +66,7 @@ import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.style.CharacterStyle;
import android.text.style.ClickableSpan;
import android.text.style.URLSpan;
import android.text.util.Linkify;
@ -122,10 +123,10 @@ import org.telegram.messenger.utils.CustomHtml;
import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.ActionBarLayout;
import org.telegram.ui.ActionBar.AlertDialog;
import org.telegram.ui.ActionBar.BaseFragment;
import org.telegram.ui.ActionBar.BottomSheet;
import org.telegram.ui.ActionBar.INavigationLayout;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Cells.TextDetailSettingsCell;
import org.telegram.ui.Components.AlertsCreator;
@ -172,6 +173,7 @@ import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -179,6 +181,9 @@ import java.util.regex.Pattern;
public class AndroidUtilities {
public final static int LIGHT_STATUS_BAR_OVERLAY = 0x0f000000, DARK_STATUS_BAR_OVERLAY = 0x33000000;
public final static int REPLACING_TAG_TYPE_LINK = 0;
public final static int REPLACING_TAG_TYPE_BOLD = 1;
public final static String TYPEFACE_ROBOTO_MEDIUM = "fonts/rmedium.ttf";
private static final Hashtable<String, Typeface> typefaceCache = new Hashtable<>();
@ -421,10 +426,10 @@ public class AndroidUtilities {
}
public static CharSequence replaceSingleTag(String str, Runnable runnable) {
return replaceSingleTag(str, null, runnable);
return replaceSingleTag(str, null, 0, runnable);
}
public static CharSequence replaceSingleTag(String str, String colorKey, Runnable runnable) {
public static CharSequence replaceSingleTag(String str, String colorKey, int type, Runnable runnable) {
int startIndex = str.indexOf("**");
int endIndex = str.indexOf("**", startIndex + 1);
str = str.replace("**", "");
@ -436,24 +441,36 @@ public class AndroidUtilities {
}
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(str);
if (index >= 0) {
spannableStringBuilder.setSpan(new ClickableSpan() {
if (type == REPLACING_TAG_TYPE_LINK) {
spannableStringBuilder.setSpan(new ClickableSpan() {
@Override
public void updateDrawState(@NonNull TextPaint ds) {
super.updateDrawState(ds);
ds.setUnderlineText(false);
if (colorKey != null) {
ds.setColor(Theme.getColor(colorKey));
@Override
public void updateDrawState(@NonNull TextPaint ds) {
super.updateDrawState(ds);
ds.setUnderlineText(false);
if (colorKey != null) {
ds.setColor(Theme.getColor(colorKey));
}
}
}
@Override
public void onClick(@NonNull View view) {
if (runnable != null) {
runnable.run();
@Override
public void onClick(@NonNull View view) {
if (runnable != null) {
runnable.run();
}
}
}
}, index, index + len, 0);
}, index, index + len, 0);
} else {
spannableStringBuilder.setSpan(new CharacterStyle() {
@Override
public void updateDrawState(TextPaint textPaint) {
textPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
int wasAlpha = textPaint.getAlpha();
textPaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueText));
textPaint.setAlpha(wasAlpha);
}
}, index, index + len, 0);
}
}
return spannableStringBuilder;
}
@ -705,6 +722,9 @@ public class AndroidUtilities {
}
public static int calcBitmapColor(Bitmap bitmap) {
if (bitmap == null) {
return 0;
}
try {
Bitmap b = Bitmaps.createScaledBitmap(bitmap, 1, 1, true);
if (b != null) {
@ -866,6 +886,30 @@ public class AndroidUtilities {
}));
}
public static void adjustHueColorMatrix(ColorMatrix cm, float value) {
value = cleanValue(value, 180f) / 180f * (float) Math.PI;
if (value == 0) {
return;
}
float cosVal = (float) Math.cos(value);
float sinVal = (float) Math.sin(value);
float lumR = 0.213f;
float lumG = 0.715f;
float lumB = 0.072f;
float[] mat = new float[]
{
lumR + cosVal * (1 - lumR) + sinVal * (-lumR), lumG + cosVal * (-lumG) + sinVal * (-lumG), lumB + cosVal * (-lumB) + sinVal * (1 - lumB), 0, 0,
lumR + cosVal * (-lumR) + sinVal * (0.143f), lumG + cosVal * (1 - lumG) + sinVal * (0.140f), lumB + cosVal * (-lumB) + sinVal * (-0.283f), 0, 0,
lumR + cosVal * (-lumR) + sinVal * (-(1 - lumR)), lumG + cosVal * (-lumG) + sinVal * (lumG), lumB + cosVal * (1 - lumB) + sinVal * (lumB), 0, 0,
0f, 0f, 0f, 1f, 0f,
0f, 0f, 0f, 0f, 1f};
cm.postConcat(new ColorMatrix(mat));
}
protected static float cleanValue(float p_val, float p_limit) {
return Math.min(p_limit, Math.max(-p_limit, p_val));
}
public static void multiplyBrightnessColorMatrix(ColorMatrix colorMatrix, float v) {
if (colorMatrix == null) {
return;
@ -2026,9 +2070,13 @@ public class AndroidUtilities {
return ch == '-' || ch == '~';
}
public static boolean isTabletForce() {
return ApplicationLoader.applicationContext != null && ApplicationLoader.applicationContext.getResources().getBoolean(R.bool.isTablet);
}
public static boolean isTabletInternal() {
if (isTablet == null) {
isTablet = ApplicationLoader.applicationContext != null && ApplicationLoader.applicationContext.getResources().getBoolean(R.bool.isTablet);
isTablet = isTabletForce();
}
return isTablet;
}
@ -2471,24 +2519,29 @@ public class AndroidUtilities {
uptime + 5 < SharedConfig.lastPauseTime);
}
public static void shakeView(final View view, final float x, final int num) {
public static void shakeView(final View view) {
if (view == null) {
return;
}
if (num == 6) {
view.setTranslationX(0);
return;
final float N = 4;
Object animator = view.getTag(R.id.shake_animation);
if (animator instanceof ValueAnimator) {
((ValueAnimator) animator).cancel();
}
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(ObjectAnimator.ofFloat(view, "translationX", dp(x)));
animatorSet.setDuration(50);
animatorSet.addListener(new AnimatorListenerAdapter() {
ValueAnimator va = ValueAnimator.ofFloat(0, 1);
va.addUpdateListener(anm -> {
float x = (float) anm.getAnimatedValue();
view.setTranslationX((float) ((4 * x * (1 - x)) * Math.sin(N * (x * Math.PI)) * AndroidUtilities.dp(N)));
});
va.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
shakeView(view, num == 5 ? 0 : -x, num + 1);
view.setTranslationX(0);
}
});
animatorSet.start();
va.setDuration(300);
va.start();
view.setTag(R.id.shake_animation, va);
}
public static void shakeViewSpring(View view) {
@ -2505,13 +2558,28 @@ public class AndroidUtilities {
public static void shakeViewSpring(View view, float shiftDp, Runnable endCallback) {
int shift = dp(shiftDp);
new SpringAnimation(view, DynamicAnimation.TRANSLATION_X, 0)
.setSpring(new SpringForce(0).setStiffness(600f))
if (view.getTag(R.id.spring_tag) != null) {
((SpringAnimation)view.getTag(R.id.spring_tag)).cancel();
}
Float wasX = (Float) view.getTag(R.id.spring_was_translation_x_tag);
if (wasX != null) {
view.setTranslationX(wasX);
}
view.setTag(R.id.spring_was_translation_x_tag, view.getTranslationX());
float translationX = view.getTranslationX();
SpringAnimation springAnimation = new SpringAnimation(view, DynamicAnimation.TRANSLATION_X, translationX)
.setSpring(new SpringForce(translationX).setStiffness(600f))
.setStartVelocity(-shift * 100)
.addEndListener((animation, canceled, value, velocity) -> {
if (endCallback != null) endCallback.run();
})
.start();
view.setTranslationX(translationX);
view.setTag(R.id.spring_tag, null);
view.setTag(R.id.spring_was_translation_x_tag, null);
});
view.setTag(R.id.spring_tag, springAnimation);
springAnimation.start();
}
/*public static String ellipsize(String text, int maxLines, int maxWidth, TextPaint paint) {
@ -3117,7 +3185,11 @@ public class AndroidUtilities {
parentFragment.presentFragment(new ThemePreviewActivity(themeInfo));
} else {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle(LocaleController.getString("AppName", R.string.AppName));
Map<String, Integer> colorsReplacement = new HashMap<>();
colorsReplacement.put("info1.**", parentFragment.getThemedColor(Theme.key_dialogTopBackground));
colorsReplacement.put("info2.**", parentFragment.getThemedColor(Theme.key_dialogTopBackground));
builder.setTopAnimation(R.raw.not_available, AlertsCreator.NEW_DENY_DIALOG_TOP_ICON_SIZE, false, parentFragment.getThemedColor(Theme.key_dialogTopBackground), colorsReplacement);
builder.setTopAnimationIsNew(true);
builder.setMessage(LocaleController.getString("IncorrectTheme", R.string.IncorrectTheme));
builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null);
parentFragment.showDialog(builder.create());
@ -3163,7 +3235,11 @@ public class AndroidUtilities {
return;
}
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle(LocaleController.getString("AppName", R.string.AppName));
Map<String, Integer> colorsReplacement = new HashMap<>();
colorsReplacement.put("info1.**", parentFragment.getThemedColor(Theme.key_dialogTopBackground));
colorsReplacement.put("info2.**", parentFragment.getThemedColor(Theme.key_dialogTopBackground));
builder.setTopAnimation(R.raw.not_available, AlertsCreator.NEW_DENY_DIALOG_TOP_ICON_SIZE, false, parentFragment.getThemedColor(Theme.key_dialogTopBackground), colorsReplacement);
builder.setTopAnimationIsNew(true);
builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null);
builder.setMessage(LocaleController.formatString("NoHandleAppInstalled", R.string.NoHandleAppInstalled, message.getDocument().mime_type));
if (parentFragment != null) {
@ -3229,7 +3305,7 @@ public class AndroidUtilities {
if (f == null || !f.exists()) {
f = FileLoader.getInstance(message.currentAccount).getPathToMessage(message.messageOwner);
}
String mimeType = message.type == 9 || message.type == 0 ? message.getMimeType() : null;
String mimeType = message.type == MessageObject.TYPE_FILE || message.type == MessageObject.TYPE_TEXT ? message.getMimeType() : null;
return openForView(f, message.getFileName(), mimeType, activity, resourcesProvider);
}
@ -4243,11 +4319,11 @@ public class AndroidUtilities {
return false;
}
public static void scrollToFragmentRow(ActionBarLayout parentLayout, String rowName) {
public static void scrollToFragmentRow(INavigationLayout parentLayout, String rowName) {
if (parentLayout == null || rowName == null) {
return;
}
BaseFragment openingFragment = parentLayout.fragmentsStack.get(parentLayout.fragmentsStack.size() - 1);
BaseFragment openingFragment = parentLayout.getFragmentStack().get(parentLayout.getFragmentStack().size() - 1);
try {
Field listViewField = openingFragment.getClass().getDeclaredField("listView");
listViewField.setAccessible(true);
@ -4304,6 +4380,9 @@ public class AndroidUtilities {
}
public static void updateImageViewImageAnimated(ImageView imageView, Drawable newIcon) {
if (imageView.getDrawable() == newIcon) {
return;
}
ValueAnimator animator = ValueAnimator.ofFloat(0, 1).setDuration(150);
AtomicBoolean changed = new AtomicBoolean();
animator.addUpdateListener(animation -> {
@ -4355,6 +4434,45 @@ public class AndroidUtilities {
}
}
public static void updateViewShow(View view, boolean show) {
updateViewShow(view, show, true, true);
}
public static void updateViewShow(View view, boolean show, boolean scale, boolean animated) {
updateViewShow(view, show, scale, animated, null);
}
public static void updateViewShow(View view, boolean show, boolean scale, boolean animated, Runnable onDone) {
if (view == null) {
return;
}
if (view.getParent() == null) {
animated = false;
}
view.animate().setListener(null).cancel();
if (!animated) {
view.setVisibility(show ? View.VISIBLE : View.GONE);
view.setTag(show ? 1 : null);
view.setAlpha(1f);
view.setScaleX(scale && !show ? 0f : 1f);
view.setScaleY(scale && !show ? 0f : 1f);
if (onDone != null) {
onDone.run();
}
} else if (show) {
if (view.getVisibility() != View.VISIBLE) {
view.setVisibility(View.VISIBLE);
view.setAlpha(0f);
view.setScaleX(scale ? 0 : 1);
view.setScaleY(scale ? 0 : 1);
}
view.animate().alpha(1f).scaleY(1f).scaleX(1f).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).setDuration(340).withEndAction(onDone).start();
} else {
view.animate().alpha(0).scaleY(scale ? 0 : 1).scaleX(scale ? 0 : 1).setListener(new HideViewAfterAnimation(view)).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).setDuration(340).withEndAction(onDone).start();
}
}
public static long getPrefIntOrLong(SharedPreferences preferences, String key, long defaultValue) {
try {
return preferences.getLong(key, defaultValue);
@ -4471,4 +4589,29 @@ public class AndroidUtilities {
}
return (st > 0 || len < text.length()) ? text.subSequence(st, len) : text;
}
// detect Error NO SPaCe left on device :(
public static boolean isENOSPC(Exception e) {
return (
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
e instanceof IOException &&
(e.getCause() instanceof ErrnoException &&
((ErrnoException) e.getCause()).errno == OsConstants.ENOSPC) ||
(e.getMessage() != null && e.getMessage().equalsIgnoreCase("no space left on device"))
);
}
public static CharSequence replaceCharSequence(String what, CharSequence from, CharSequence obj) {
SpannableStringBuilder spannableStringBuilder;
if (from instanceof SpannableStringBuilder) {
spannableStringBuilder = (SpannableStringBuilder) from;
} else {
spannableStringBuilder = new SpannableStringBuilder(from);
}
int index = TextUtils.indexOf(from, what);
if (index >= 0) {
spannableStringBuilder.replace(index, index + what.length(), obj);
}
return spannableStringBuilder;
}
}

View File

@ -14,7 +14,8 @@ public enum BotWebViewVibrationEffect {
NOTIFICATION_ERROR(new long[] {14,48,14,48,14,48,20}, new int[] {200,0,200,0,255,0,145}, new long[] {40,60,40,60,65,60,40}),
NOTIFICATION_SUCCESS(new long[] {14,65,14}, new int[] {175,0,255}, new long[] {50,60,65}),
NOTIFICATION_WARNING(new long[] {14,64,14}, new int[] {225,0,175}, new long[] {65,60,40}),
SELECTION_CHANGE(new long[] {1}, new int[] {65}, new long[] {30});
SELECTION_CHANGE(new long[] {1}, new int[] {65}, new long[] {30}),
APP_ERROR(new long[] {30,10,150,10}, new int[] {0,100,0,100}, new long[] {40,60,40,60,65,60,40});
public final long[] timings;
public final int[] amplitudes;
@ -39,4 +40,12 @@ public enum BotWebViewVibrationEffect {
return (VibrationEffect) vibrationEffect;
}
public void vibrate() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
AndroidUtilities.getVibrator().vibrate(getVibrationEffectForOreo());
} else {
AndroidUtilities.getVibrator().vibrate(fallbackTimings, -1);
}
}
}

View File

@ -24,8 +24,8 @@ public class BuildVars {
public static boolean USE_CLOUD_STRINGS = true;
public static boolean CHECK_UPDATES = true;
public static boolean NO_SCOPED_STORAGE = Build.VERSION.SDK_INT <= 29;
public static int BUILD_VERSION = 2808;
public static String BUILD_VERSION_STRING = "9.0.2";
public static int BUILD_VERSION = 2885;
public static String BUILD_VERSION_STRING = "9.1.0";
public static int APP_ID = 4;
public static String APP_HASH = "014b35b6184100b085b0d0572f9b5103";

View File

@ -25,6 +25,7 @@ import org.telegram.ui.GroupCallActivity;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@ -54,6 +55,7 @@ public class ChatObject {
public static final int ACTION_EDIT_MESSAGES = 12;
public static final int ACTION_DELETE_MESSAGES = 13;
public static final int ACTION_MANAGE_CALLS = 14;
public static final int ACTION_MANAGE_TOPICS = 15;
public final static int VIDEO_FRAME_NO_FRAME = 0;
public final static int VIDEO_FRAME_REQUESTING = 1;
@ -76,6 +78,14 @@ public class ChatObject {
return false;
}
public static boolean isForum(int currentAccount, long dialogId) {
TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-dialogId);
if (chat != null) {
return chat.forum;
}
return false;
}
public static class Call {
public final static int RECORD_TYPE_AUDIO = 0,
RECORD_TYPE_VIDEO_PORTAIT = 1,
@ -1402,6 +1412,7 @@ public class ChatObject {
case ACTION_EMBED_LINKS:
case ACTION_SEND_POLLS:
case ACTION_VIEW:
case ACTION_MANAGE_TOPICS:
return true;
}
return false;
@ -1417,6 +1428,7 @@ public class ChatObject {
case ACTION_EDIT_MESSAGES:
case ACTION_DELETE_MESSAGES:
case ACTION_BLOCK_USERS:
case ACTION_MANAGE_TOPICS:
return true;
}
return false;
@ -1446,6 +1458,8 @@ public class ChatObject {
return rights.send_polls;
case ACTION_VIEW:
return rights.view_messages;
case ACTION_MANAGE_TOPICS:
return rights.manage_topics;
}
return false;
}
@ -1474,6 +1488,9 @@ public class ChatObject {
case ACTION_PIN:
value = chat.admin_rights.pin_messages;
break;
case ACTION_MANAGE_TOPICS:
value = chat.admin_rights.manage_topics;
break;
case ACTION_CHANGE_INFO:
value = chat.admin_rights.change_info;
break;
@ -1556,7 +1573,7 @@ public class ChatObject {
}
public static boolean canSendAsPeers(TLRPC.Chat chat) {
return ChatObject.isChannel(chat) && chat.megagroup && (!TextUtils.isEmpty(chat.username) || chat.has_geo || chat.has_link);
return ChatObject.isChannel(chat) && chat.megagroup && (ChatObject.isPublic(chat) || chat.has_geo || chat.has_link);
}
public static boolean isChannel(TLRPC.Chat chat) {
@ -1575,6 +1592,10 @@ public class ChatObject {
return isChannel(chat) && !isMegagroup(chat);
}
public static boolean isForum(TLRPC.Chat chat) {
return chat != null && chat.forum;
}
public static boolean isMegagroup(int currentAccount, long chatId) {
TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(chatId);
return ChatObject.isChannel(chat) && chat.megagroup;
@ -1664,6 +1685,33 @@ public class ChatObject {
return canUserDoAction(chat, ACTION_PIN) || ChatObject.isChannel(chat) && !chat.megagroup && chat.admin_rights != null && chat.admin_rights.edit_messages;
}
public static boolean canCreateTopic(TLRPC.Chat chat) {
return canUserDoAction(chat, ACTION_MANAGE_TOPICS);
}
public static boolean canManageTopics(TLRPC.Chat chat) {
return canUserDoAdminAction(chat, ACTION_MANAGE_TOPICS);
}
public static boolean canManageTopic(int currentAccount, TLRPC.Chat chat, TLRPC.TL_forumTopic topic) {
return canManageTopics(chat) || isMyTopic(currentAccount, topic);
}
public static boolean canManageTopic(int currentAccount, TLRPC.Chat chat, int topicId) {
return canManageTopics(chat) || isMyTopic(currentAccount, chat, topicId);
}
public static boolean isMyTopic(int currentAccount, TLRPC.TL_forumTopic topic) {
return topic != null && (topic.my || topic.from_id instanceof TLRPC.TL_peerUser && topic.from_id.user_id == UserConfig.getInstance(currentAccount).clientUserId);
}
public static boolean isMyTopic(int currentAccount, TLRPC.Chat chat, int topicId) {
return chat != null && chat.forum && isMyTopic(currentAccount, chat.id, topicId);
}
public static boolean isMyTopic(int currentAccount, long chatId, int topicId) {
return isMyTopic(currentAccount, MessagesController.getInstance(currentAccount).getTopicsController().findTopic(chatId, topicId));
}
public static boolean isChannel(long chatId, int currentAccount) {
TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(chatId);
return chat instanceof TLRPC.TL_channel || chat instanceof TLRPC.TL_channelForbidden;
@ -1697,6 +1745,7 @@ public class ChatObject {
currentBannedRights += bannedRights.invite_users ? 1 : 0;
currentBannedRights += bannedRights.change_info ? 1 : 0;
currentBannedRights += bannedRights.pin_messages ? 1 : 0;
currentBannedRights += bannedRights.manage_topics ? 1 : 0;
currentBannedRights += bannedRights.until_date;
return currentBannedRights;
}
@ -1709,6 +1758,53 @@ public class ChatObject {
return hasPhoto(chat) ? chat.photo : null;
}
public static String getPublicUsername(TLRPC.Chat chat) {
return getPublicUsername(chat, false);
}
public static String getPublicUsername(TLRPC.Chat chat, boolean editable) {
if (chat == null) {
return null;
}
if (!TextUtils.isEmpty(chat.username) && !editable) {
return chat.username;
}
if (chat.usernames != null) {
for (int i = 0; i < chat.usernames.size(); ++i) {
TLRPC.TL_username u = chat.usernames.get(i);
if (u != null && (u.active && !editable || u.editable) && !TextUtils.isEmpty(u.username)) {
return u.username;
}
}
}
if (!TextUtils.isEmpty(chat.username) && editable && (chat.usernames == null || chat.usernames.size() <= 0)) {
return chat.username;
}
return null;
}
public static boolean hasPublicLink(TLRPC.Chat chat, String username) {
if (chat == null) {
return false;
}
if (!TextUtils.isEmpty(chat.username)) {
return chat.username.equalsIgnoreCase(username);
}
if (chat.usernames != null) {
for (int i = 0; i < chat.usernames.size(); ++i) {
TLRPC.TL_username u = chat.usernames.get(i);
if (u != null && u.active && !TextUtils.isEmpty(u.username) && u.username.equalsIgnoreCase(username)) {
return true;
}
}
}
return false;
}
public static boolean isPublic(TLRPC.Chat chat) {
return !TextUtils.isEmpty(getPublicUsername(chat));
}
public static class VideoParticipant {
public TLRPC.TL_groupCallParticipant participant;

View File

@ -242,7 +242,7 @@ class ChatsRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
} else {
innerMessage = String.format("\uD83C\uDFAE %s", message.messageOwner.media.game.title);
}
} else if (message.type == 14) {
} else if (message.type == MessageObject.TYPE_MUSIC) {
if (Build.VERSION.SDK_INT >= 18) {
innerMessage = String.format("\uD83C\uDFA7 \u2068%s - %s\u2069", message.getMusicAuthor(), message.getMusicTitle());
} else {
@ -299,7 +299,7 @@ class ChatsRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
messageString = "\uD83D\uDCCA " + mediaPoll.poll.question;
} else if (message.messageOwner.media instanceof TLRPC.TL_messageMediaGame) {
messageString = "\uD83C\uDFAE " + message.messageOwner.media.game.title;
} else if (message.type == 14) {
} else if (message.type == MessageObject.TYPE_MUSIC) {
messageString = String.format("\uD83C\uDFA7 %s - %s", message.getMusicAuthor(), message.getMusicTitle());
} else {
messageString = message.messageText;
@ -326,7 +326,7 @@ class ChatsRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
if (dialog != null && dialog.unread_count > 0) {
rv.setTextViewText(R.id.shortcut_widget_item_badge, String.format("%d", dialog.unread_count));
rv.setViewVisibility(R.id.shortcut_widget_item_badge, View.VISIBLE);
if (accountInstance.getMessagesController().isDialogMuted(dialog.id)) {
if (accountInstance.getMessagesController().isDialogMuted(dialog.id, 0)) {
rv.setBoolean(R.id.shortcut_widget_item_badge, "setEnabled", false);
rv.setInt(R.id.shortcut_widget_item_badge, "setBackgroundResource", R.drawable.widget_badge_muted_background);
} else {

View File

@ -24,6 +24,7 @@ import org.telegram.SQLite.SQLitePreparedStatement;
import org.telegram.tgnet.NativeByteBuffer;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.Components.Bulletin;
import org.telegram.ui.LaunchActivity;
import java.io.File;
import java.lang.ref.WeakReference;
@ -1248,6 +1249,8 @@ public class DownloadController extends BaseController implements NotificationCe
getNotificationCenter().postNotificationName(NotificationCenter.onDownloadingFilesChanged);
if (reason == 0) {
NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.showBulletin, Bulletin.TYPE_ERROR, LocaleController.formatString("MessageNotFound", R.string.MessageNotFound));
} else if (reason == -1) {
LaunchActivity.checkFreeDiscSpaceStatic(2);
}
}
});

View File

@ -78,7 +78,7 @@ class FeedRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory, N
String name;
RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.feed_widget_item);
if (messageObject.type == 0) {
if (messageObject.type == MessageObject.TYPE_TEXT) {
rv.setTextViewText(R.id.feed_widget_item_text, messageObject.messageText);
rv.setViewVisibility(R.id.feed_widget_item_text, View.VISIBLE);
} else {
@ -143,7 +143,7 @@ class FeedRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory, N
if (classGuid == 0) {
classGuid = ConnectionsManager.generateClassGuid();
}
accountInstance.getMessagesController().loadMessages(dialogId, 0, false, 20, 0, 0, true, 0, classGuid, 0, 0, 0, 0, 0, 1);
accountInstance.getMessagesController().loadMessages(dialogId, 0, false, 20, 0, 0, true, 0, classGuid, 0, 0, 0, 0, 0, 1, false);
});
try {
countDownLatch.await();

View File

@ -12,6 +12,7 @@ import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.NativeByteBuffer;
import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.LaunchActivity;
import java.io.File;
import java.io.FileInputStream;
@ -525,7 +526,11 @@ public class FileLoadOperation {
}
}
} catch (Exception e) {
FileLog.e(e);
if (AndroidUtilities.isENOSPC(e)) {
LaunchActivity.checkFreeDiscSpaceStatic(1);
} else {
FileLog.e(e);
}
}
totalTime += System.currentTimeMillis() - time;
});
@ -853,7 +858,11 @@ public class FileLoadOperation {
}
file.close();
} catch (Exception e) {
FileLog.e(e);
if (AndroidUtilities.isENOSPC(e)) {
LaunchActivity.checkFreeDiscSpaceStatic(1);
} else {
FileLog.e(e);
}
}
}
@ -913,7 +922,7 @@ public class FileLoadOperation {
}
preloadStream.seek(preloadStreamFileOffset);
} catch (Exception e) {
FileLog.e(e);
FileLog.e(e, false);
}
if (!isPreloadVideoOperation && preloadedBytesRanges == null) {
cacheFilePreload = null;
@ -1006,8 +1015,12 @@ public class FileLoadOperation {
}
}
} catch (Exception e) {
FileLog.e(e);
requestedBytesCount = downloadedBytes = 0;
if (AndroidUtilities.isENOSPC(e)) {
LaunchActivity.checkFreeDiscSpaceStatic(1);
} else {
FileLog.e(e);
}
}
}
if (!isPreloadVideoOperation && downloadedBytes != 0 && totalBytesCount > 0) {
@ -1020,7 +1033,13 @@ public class FileLoadOperation {
fileOutputStream.seek(downloadedBytes);
}
} catch (Exception e) {
FileLog.e(e, false);
if (AndroidUtilities.isENOSPC(e)) {
LaunchActivity.checkFreeDiscSpaceStatic(1);
onFail(true, -1);
return false;
} else {
FileLog.e(e, false);
}
}
if (fileOutputStream == null) {
onFail(true, 0);
@ -1046,7 +1065,13 @@ public class FileLoadOperation {
delegate.saveFilePath(pathSaveData, null);
}
} catch (Exception e) {
onFail(true, 0);
if (AndroidUtilities.isENOSPC(e)) {
LaunchActivity.checkFreeDiscSpaceStatic(1);
onFail(true, -1);
} else {
FileLog.e(e, false);
onFail(true, 0);
}
}
}
return true;
@ -1473,7 +1498,7 @@ public class FileLoadOperation {
protected boolean processRequestResult(RequestInfo requestInfo, TLRPC.TL_error error) {
if (state != stateDownloading) {
if (BuildVars.DEBUG_VERSION && state == stateFinished) {
FileLog.e(new Exception("trying to write to finished file " + fileName + " offset " + requestInfo.offset + " " + totalBytesCount));
FileLog.e(new FileLog.IgnoreSentException("trying to write to finished file " + fileName + " offset " + requestInfo.offset + " " + totalBytesCount));
}
return false;
}
@ -1692,8 +1717,12 @@ public class FileLoadOperation {
startDownloadRequest();
}
} catch (Exception e) {
onFail(false, 0);
FileLog.e(e);
if (AndroidUtilities.isENOSPC(e)) {
onFail(false, -1);
} else {
FileLog.e(e);
onFail(false, 0);
}
}
} else {
if (error.text.contains("FILE_MIGRATE_")) {

View File

@ -13,6 +13,7 @@ import android.util.SparseArray;
import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.LaunchActivity;
import java.io.File;
import java.io.FileOutputStream;
@ -736,6 +737,8 @@ public class FileLoader extends BaseController {
if (document != null && parentObject instanceof MessageObject && reason == 0) {
getDownloadController().onDownloadFail((MessageObject) parentObject, reason);
} else if (reason == -1) {
LaunchActivity.checkFreeDiscSpaceStatic(2);
}
}

View File

@ -12,6 +12,7 @@ import android.util.Log;
import org.telegram.messenger.time.FastDateFormat;
import org.telegram.messenger.video.MediaCodecVideoConvertor;
import org.telegram.ui.LaunchActivity;
import java.io.File;
import java.io.FileOutputStream;
@ -228,7 +229,7 @@ public class FileLog {
}
private static boolean needSent(Throwable e) {
if (e instanceof InterruptedException || e instanceof MediaCodecVideoConvertor.ConversionCanceledException) {
if (e instanceof InterruptedException || e instanceof MediaCodecVideoConvertor.ConversionCanceledException || e instanceof IgnoreSentException) {
return false;
}
return true;
@ -247,6 +248,9 @@ public class FileLog {
getInstance().streamWriter.flush();
} catch (Exception e) {
e.printStackTrace();
if (AndroidUtilities.isENOSPC(e)) {
LaunchActivity.checkFreeDiscSpaceStatic(1);
}
}
});
}
@ -294,4 +298,12 @@ public class FileLog {
}
}
}
public static class IgnoreSentException extends Exception{
public IgnoreSentException(String e) {
super(e);
}
}
}

View File

@ -43,10 +43,8 @@ public class FilePathDatabase {
cacheFile = new File(filesDir, DATABASE_NAME + ".db");
shmCacheFile = new File(filesDir, DATABASE_NAME + ".db-shm");
boolean createTable = false;
if (!cacheFile.exists()) {
createTable = true;
}
@ -145,20 +143,22 @@ public class FilePathDatabase {
long time = System.currentTimeMillis();
dispatchQueue.postRunnable(() -> {
SQLiteCursor cursor = null;
try {
cursor = database.queryFinalized("SELECT path FROM paths WHERE document_id = " + documentId + " AND dc_id = " + dc + " AND type = " + type);
if (cursor.next()) {
res[0] = cursor.stringValue(0);
if (BuildVars.DEBUG_VERSION) {
FileLog.d("get file path id=" + documentId + " dc=" + dc + " type=" + type + " path=" + res[0]);
if (database != null) {
SQLiteCursor cursor = null;
try {
cursor = database.queryFinalized("SELECT path FROM paths WHERE document_id = " + documentId + " AND dc_id = " + dc + " AND type = " + type);
if (cursor.next()) {
res[0] = cursor.stringValue(0);
if (BuildVars.DEBUG_VERSION) {
FileLog.d("get file path id=" + documentId + " dc=" + dc + " type=" + type + " path=" + res[0]);
}
}
} catch (SQLiteException e) {
FileLog.e(e);
} finally {
if (cursor != null) {
cursor.dispose();
}
}
} catch (SQLiteException e) {
FileLog.e(e);
} finally {
if (cursor != null) {
cursor.dispose();
}
}
syncLatch.countDown();
@ -169,6 +169,9 @@ public class FilePathDatabase {
}
return res[0];
} else {
if (database == null) {
return null;
}
SQLiteCursor cursor = null;
String res = null;
try {
@ -195,6 +198,9 @@ public class FilePathDatabase {
if (BuildVars.DEBUG_VERSION) {
FileLog.d("put file path id=" + id + " dc=" + dc + " type=" + type + " path=" + path);
}
if (database == null) {
return;
}
SQLitePreparedStatement state = null;
SQLitePreparedStatement deleteState = null;
try {
@ -266,7 +272,7 @@ public class FilePathDatabase {
dispatchQueue.postRunnable(() -> {
try {
database.executeFast("DELETE FROM paths WHERE 1").stepThis().dispose();
} catch (SQLiteException e) {
} catch (Exception e) {
FileLog.e(e);
}
});

View File

@ -1519,7 +1519,7 @@ public class ImageLoader {
}
}
} catch (Throwable e) {
FileLog.e(e);
FileLog.e(e, !(e instanceof FileNotFoundException));
}
}
Thread.interrupted();
@ -1535,12 +1535,18 @@ public class ImageLoader {
}
private void loadLastFrame(RLottieDrawable lottieDrawable, int w, int h, boolean lastFrame) {
Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Bitmap bitmap;
Canvas canvas;
if (lastFrame) {
canvas.scale(2f, 2f, w / 2f, h / 2f);
bitmap = Bitmap.createBitmap((int) (w * ImageReceiver.ReactionLastFrame.LAST_FRAME_SCALE), (int) (h * ImageReceiver.ReactionLastFrame.LAST_FRAME_SCALE), Bitmap.Config.ARGB_8888);
canvas = new Canvas(bitmap);
canvas.scale(2f, 2f, w * ImageReceiver.ReactionLastFrame.LAST_FRAME_SCALE / 2f, h * ImageReceiver.ReactionLastFrame.LAST_FRAME_SCALE / 2f);
} else {
bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
canvas = new Canvas(bitmap);
}
AndroidUtilities.runOnUIThread(() -> {
lottieDrawable.setOnFrameReadyRunnable(() -> {
lottieDrawable.setOnFrameReadyRunnable(null);
@ -1553,8 +1559,13 @@ public class ImageLoader {
}
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setFilterBitmap(true);
canvas.drawBitmap(currentBitmap, 0, 0, paint);
bitmapDrawable = new BitmapDrawable(bitmap);
if (lastFrame) {
canvas.drawBitmap(currentBitmap, (bitmap.getWidth() - currentBitmap.getWidth()) / 2f, (bitmap.getHeight() - currentBitmap.getHeight()) / 2f, paint);
bitmapDrawable = new ImageReceiver.ReactionLastFrame(bitmap);
} else {
canvas.drawBitmap(currentBitmap, 0, 0, paint);
bitmapDrawable = new BitmapDrawable(bitmap);
}
}
onPostExecute(bitmapDrawable);
lottieDrawable.recycle();

View File

@ -83,6 +83,17 @@ public class ImageLocation {
return getForPhoto(photoSize, (TLRPC.Photo) object);
} else if (object instanceof TLRPC.Document) {
return getForDocument(photoSize, (TLRPC.Document) object);
} else if (object instanceof TLRPC.Message) {
return getForMessage(photoSize, (TLRPC.Message) object);
}
return null;
}
public static ImageLocation getForMessage(TLRPC.PhotoSize photoSize, TLRPC.Message message) {
if (photoSize instanceof TLRPC.TL_photoStrippedSize || photoSize instanceof TLRPC.TL_photoPathSize) {
ImageLocation imageLocation = new ImageLocation();
imageLocation.photoSize = photoSize;
return imageLocation;
}
return null;
}

View File

@ -33,6 +33,7 @@ import androidx.annotation.Keep;
import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.Components.AnimatedFileDrawable;
import org.telegram.ui.Components.AvatarDrawable;
import org.telegram.ui.Components.LoadingStickerDrawable;
import org.telegram.ui.Components.RLottieDrawable;
import org.telegram.ui.Components.RecyclableDrawable;
@ -177,6 +178,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
private int currentLayerNum;
private int currentOpenedLayerFlags;
private int isLastFrame;
private SetImageBackup setImageBackup;
@ -486,6 +488,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
if (staticThumbDrawable instanceof SvgHelper.SvgDrawable) {
((SvgHelper.SvgDrawable) staticThumbDrawable).setParent(this);
}
updateDrawableRadius(staticThumbDrawable);
ImageLoader.getInstance().cancelLoadingForImageReceiver(this, true);
invalidate();
@ -883,15 +886,19 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
if (drawable == null) {
return;
}
if ((hasRoundRadius() || gradientShader != null) && drawable instanceof BitmapDrawable) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
if (bitmapDrawable instanceof RLottieDrawable) {
if ((hasRoundRadius() || gradientShader != null) && (drawable instanceof BitmapDrawable || drawable instanceof AvatarDrawable)) {
if (drawable instanceof AvatarDrawable) {
((AvatarDrawable) drawable).setRoundRadius(roundRadius[0]);
} else {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
if (bitmapDrawable instanceof RLottieDrawable) {
} else if (bitmapDrawable instanceof AnimatedFileDrawable) {
AnimatedFileDrawable animatedFileDrawable = (AnimatedFileDrawable) drawable;
animatedFileDrawable.setRoundRadius(roundRadius);
} else if (bitmapDrawable.getBitmap() != null) {
setDrawableShader(drawable, new BitmapShader(bitmapDrawable.getBitmap(), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
} else if (bitmapDrawable instanceof AnimatedFileDrawable) {
AnimatedFileDrawable animatedFileDrawable = (AnimatedFileDrawable) drawable;
animatedFileDrawable.setRoundRadius(roundRadius);
} else if (bitmapDrawable.getBitmap() != null) {
setDrawableShader(drawable, new BitmapShader(bitmapDrawable.getBitmap(), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
}
}
} else {
setDrawableShader(drawable, null);
@ -1027,6 +1034,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
RectF drawRegion;
ColorFilter colorFilter;
int[] roundRadius;
boolean reactionLastFrame = false;
if (backgroundThreadDrawHolder != null) {
imageX = backgroundThreadDrawHolder.imageX;
imageY = backgroundThreadDrawHolder.imageY;
@ -1116,12 +1124,17 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
bitmapW = bitmap.getWidth();
bitmapH = bitmap.getHeight();
}
reactionLastFrame = bitmapDrawable instanceof ReactionLastFrame;
}
float realImageW = imageW - sideClip * 2;
float realImageH = imageH - sideClip * 2;
float scaleW = imageW == 0 ? 1.0f : (bitmapW / realImageW);
float scaleH = imageH == 0 ? 1.0f : (bitmapH / realImageH);
if (reactionLastFrame) {
scaleW /= ReactionLastFrame.LAST_FRAME_SCALE;
scaleH /= ReactionLastFrame.LAST_FRAME_SCALE;
}
if (shader != null && backgroundThreadDrawHolder == null) {
if (isAspectFit) {
float scale = Math.max(scaleW, scaleH);
@ -1192,7 +1205,11 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
}
if (isVisible) {
shaderMatrix.reset();
shaderMatrix.setTranslate((int) (drawRegion.left + sideClip), (int) (drawRegion.top + sideClip));
if (reactionLastFrame) {
shaderMatrix.setTranslate((int) (drawRegion.left + sideClip) - (drawRegion.width() * ReactionLastFrame.LAST_FRAME_SCALE - drawRegion.width()) / 2f, (int) (drawRegion.top + sideClip) - (drawRegion.height() * ReactionLastFrame.LAST_FRAME_SCALE - drawRegion.height()) / 2f);
} else {
shaderMatrix.setTranslate((int) (drawRegion.left + sideClip), (int) (drawRegion.top + sideClip));
}
if (orientation == 90) {
shaderMatrix.preRotate(90);
shaderMatrix.preTranslate(0, -drawRegion.width());
@ -1242,7 +1259,13 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
if (isRoundRect) {
try {
if (roundRadius[0] == 0) {
canvas.drawRect(roundRect, roundPaint);
if (reactionLastFrame) {
AndroidUtilities.rectTmp.set(roundRect);
AndroidUtilities.rectTmp.inset(-(drawRegion.width() * ReactionLastFrame.LAST_FRAME_SCALE - drawRegion.width()) / 2f, -(drawRegion.height() * ReactionLastFrame.LAST_FRAME_SCALE - drawRegion.height()) / 2f);
canvas.drawRect(AndroidUtilities.rectTmp, roundPaint);
} else {
canvas.drawRect(roundRect, roundPaint);
}
} else {
canvas.drawRoundRect(roundRect, roundRadius[0], roundRadius[0], roundPaint);
}
@ -2211,7 +2234,8 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
}
if (currentThumbDrawable != null) {
updateDrawableRadius(currentThumbDrawable);
} else if (staticThumbDrawable != null) {
}
if (staticThumbDrawable != null) {
updateDrawableRadius(staticThumbDrawable);
}
}
@ -2922,4 +2946,13 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
}
}
}
public static class ReactionLastFrame extends BitmapDrawable {
public final static float LAST_FRAME_SCALE = 1.2f;
public ReactionLastFrame(Bitmap bitmap) {
super(bitmap);
}
}
}

View File

@ -1063,6 +1063,10 @@ public class LocaleController {
}
public static String formatPluralStringComma(String key, int plural) {
return formatPluralStringComma(key, plural, ',');
}
public static String formatPluralStringComma(String key, int plural, char symbol) {
try {
if (key == null || key.length() == 0 || getInstance().currentPluralRules == null) {
return "LOC_ERR:" + key;
@ -1071,7 +1075,7 @@ public class LocaleController {
param = key + "_" + param;
StringBuilder stringBuilder = new StringBuilder(String.format(Locale.US, "%d", plural));
for (int a = stringBuilder.length() - 3; a > 0; a -= 3) {
stringBuilder.insert(a, ',');
stringBuilder.insert(a, symbol);
}
String value = BuildVars.USE_CLOUD_STRINGS ? getInstance().localeValues.get(param) : null;

View File

@ -52,6 +52,7 @@ import android.provider.OpenableColumns;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import android.view.HapticFeedbackConstants;
import android.view.TextureView;
@ -520,6 +521,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
public static ArrayList<AlbumEntry> allPhotoAlbums = new ArrayList<>();
private static Runnable broadcastPhotosRunnable;
public boolean isSilent = false;
private boolean isPaused = false;
private VideoPlayer audioPlayer = null;
private VideoPlayer emojiSoundPlayer = null;
@ -1044,7 +1046,9 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
private void setPlayerVolume() {
try {
float volume;
if (audioFocus != AUDIO_NO_FOCUS_CAN_DUCK) {
if (isSilent) {
volume = 0;
} else if (audioFocus != AUDIO_NO_FOCUS_CAN_DUCK) {
volume = VOLUME_NORMAL;
} else {
volume = VOLUME_DUCK;
@ -2126,12 +2130,13 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
return;
}
//TODO topics
if (!playlistEndReached[0]) {
loadingPlaylist = true;
AccountInstance.getInstance(playingMessageObject.currentAccount).getMediaDataController().loadMedia(playingMessageObject.getDialogId(), 50, playlistMaxId[0], 0, MediaDataController.MEDIA_MUSIC, 1, playlistClassGuid, 0);
AccountInstance.getInstance(playingMessageObject.currentAccount).getMediaDataController().loadMedia(playingMessageObject.getDialogId(), 50, playlistMaxId[0], 0, MediaDataController.MEDIA_MUSIC, 0, 1, playlistClassGuid, 0);
} else if (playlistMergeDialogId != 0 && !playlistEndReached[1]) {
loadingPlaylist = true;
AccountInstance.getInstance(playingMessageObject.currentAccount).getMediaDataController().loadMedia(playlistMergeDialogId, 50, playlistMaxId[0], 0, MediaDataController.MEDIA_MUSIC, 1, playlistClassGuid, 0);
AccountInstance.getInstance(playingMessageObject.currentAccount).getMediaDataController().loadMedia(playlistMergeDialogId, 50, playlistMaxId[0], 0, MediaDataController.MEDIA_MUSIC, 0, 1, playlistClassGuid, 0);
}
}
@ -2443,6 +2448,10 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
}
public boolean isPiPShown() {
return pipRoundVideoView != null;
}
public void setCurrentVideoVisible(boolean visible) {
if (currentAspectRatioFrameLayout == null) {
return;
@ -2479,6 +2488,10 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
public void setTextureView(TextureView textureView, AspectRatioFrameLayout aspectRatioFrameLayout, FrameLayout container, boolean set) {
setTextureView(textureView, aspectRatioFrameLayout, container, set, false);
}
public void setTextureView(TextureView textureView, AspectRatioFrameLayout aspectRatioFrameLayout, FrameLayout container, boolean set, boolean forcePip) {
if (textureView == null) {
return;
}
@ -2494,6 +2507,16 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
isDrawingWasReady = aspectRatioFrameLayout != null && aspectRatioFrameLayout.isDrawingReady();
currentTextureView = textureView;
if (forcePip && pipRoundVideoView == null) {
try {
pipRoundVideoView = new PipRoundVideoView();
AndroidUtilities.runOnUIThread(() -> {
pipRoundVideoView.show(baseActivity, () -> cleanupPlayer(true, true));
}, 350);
} catch (Exception e) {
pipRoundVideoView = null;
}
}
if (pipRoundVideoView != null) {
videoPlayer.setTextureView(pipRoundVideoView.getTextureView());
} else {
@ -2839,6 +2862,9 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
private static long volumeBarLastTimeShown;
public void checkVolumeBarUI() {
if (isSilent) {
return;
}
try {
final long now = System.currentTimeMillis();
if (Math.abs(now - volumeBarLastTimeShown) < 5000) {
@ -2874,9 +2900,14 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
public boolean playMessage(final MessageObject messageObject) {
return playMessage(messageObject, false);
}
public boolean playMessage(final MessageObject messageObject, boolean silent) {
if (messageObject == null) {
return false;
}
isSilent = silent;
checkVolumeBarUI();
if ((audioPlayer != null || videoPlayer != null) && isSamePlayingMessage(messageObject)) {
if (isPaused) {
@ -2958,6 +2989,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
int[] playCount = isVideo && messageObject.getDuration() <= 30 ? new int[]{1} : null;
clearPlaylist();
videoPlayer = new VideoPlayer();
videoPlayer.setLooping(silent);
int tag = ++playerNum;
videoPlayer.setDelegate(new VideoPlayer.VideoPlayerDelegate() {
@Override
@ -3331,6 +3363,18 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
return true;
}
public void updateSilent(boolean value) {
isSilent = value;
if (videoPlayer != null) {
videoPlayer.setLooping(value);
}
setPlayerVolume();
checkVolumeBarUI();
if (playingMessageObject != null) {
NotificationCenter.getInstance(playingMessageObject.currentAccount).postNotificationName(NotificationCenter.messagePlayingPlayStateChanged, playingMessageObject != null ? playingMessageObject.getId() : 0);
}
}
public AudioInfo getAudioInfo() {
return audioInfo;
@ -3413,7 +3457,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
audioVolumeAnimator.removeAllListeners();
audioVolumeAnimator.cancel();
}
if (!messageObject.isVoice()) {
if (!messageObject.isVoice() && !messageObject.isRoundVideo()) {
audioVolumeAnimator = ValueAnimator.ofFloat(audioVolume, 1f);
audioVolumeAnimator.addUpdateListener(audioVolumeUpdateListener);
audioVolumeAnimator.setDuration(300);
@ -3604,7 +3648,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
TLRPC.TL_messages_messages messagesRes = new TLRPC.TL_messages_messages();
messagesRes.messages.add(messageObject1.messageOwner);
MessagesStorage.getInstance(messageObject1.currentAccount).putMessages(messagesRes, messageObject1.getDialogId(), -1, 0, false, messageObject.scheduled);
MessagesStorage.getInstance(messageObject1.currentAccount).putMessages(messagesRes, messageObject1.getDialogId(), -1, 0, false, messageObject.scheduled, 0);
ArrayList<MessageObject> arrayList = new ArrayList<>();
arrayList.add(messageObject1);
NotificationCenter.getInstance(messageObject1.currentAccount).postNotificationName(NotificationCenter.replaceMessagesObjects, messageObject1.getDialogId(), arrayList);

View File

@ -65,7 +65,6 @@ import org.telegram.ui.Components.Bulletin;
import org.telegram.ui.Components.ChatThemeBottomSheet;
import org.telegram.ui.Components.RLottieDrawable;
import org.telegram.ui.Components.Reactions.ReactionsEffectOverlay;
import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble;
import org.telegram.ui.Components.Reactions.ReactionsUtils;
import org.telegram.ui.Components.StickerSetBulletinLayout;
import org.telegram.ui.Components.StickersArchiveAlert;
@ -258,6 +257,7 @@ public class MediaDataController extends BaseController {
private boolean loadingPremiumGiftStickers;
private boolean loadingGenericAnimations;
private boolean loadingDefaultTopicIcons;
private long loadFeaturedHash[] = new long[2];
private int loadFeaturedDate[] = new int[2];
@ -598,12 +598,17 @@ public class MediaDataController extends BaseController {
c.dispose();
}
}
processLoadedReactions(reactions, hash, date, true);
List<TLRPC.TL_availableReaction> finalReactions = reactions;
int finalHash = hash;
int finalDate = date;
AndroidUtilities.runOnUIThread(() -> {
processLoadedReactions(finalReactions, finalHash, finalDate, true);
});
});
} else {
TLRPC.TL_messages_getAvailableReactions req = new TLRPC.TL_messages_getAvailableReactions();
req.hash = force ? 0 : reactionsUpdateHash;
getConnectionsManager().sendRequest(req, (response, error) -> {
getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> {
int date = (int) (System.currentTimeMillis() / 1000);
if (response instanceof TLRPC.TL_messages_availableReactionsNotModified) {
processLoadedReactions(null, 0, date, false);
@ -611,7 +616,7 @@ public class MediaDataController extends BaseController {
TLRPC.TL_messages_availableReactions r = (TLRPC.TL_messages_availableReactions) response;
processLoadedReactions(r.reactions, r.hash, date, false);
}
});
}));
}
}
@ -2048,7 +2053,7 @@ public class MediaDataController extends BaseController {
private void processLoadStickersResponse(int type, TLRPC.TL_messages_allStickers res, Runnable onDone) {
ArrayList<TLRPC.TL_messages_stickerSet> newStickerArray = new ArrayList<>();
if (res.sets.isEmpty()) {
processLoadedStickers(type, newStickerArray, false, (int) (System.currentTimeMillis() / 1000), res.hash, onDone);
processLoadedStickers(type, newStickerArray, false, (int) (System.currentTimeMillis() / 1000), res.hash2, onDone);
} else {
LongSparseArray<TLRPC.TL_messages_stickerSet> newStickerSets = new LongSparseArray<>();
for (int a = 0; a < res.sets.size(); a++) {
@ -2063,7 +2068,7 @@ public class MediaDataController extends BaseController {
newStickerArray.add(oldSet);
if (newStickerSets.size() == res.sets.size()) {
processLoadedStickers(type, newStickerArray, false, (int) (System.currentTimeMillis() / 1000), res.hash);
processLoadedStickers(type, newStickerArray, false, (int) (System.currentTimeMillis() / 1000), res.hash2);
}
continue;
}
@ -2087,7 +2092,7 @@ public class MediaDataController extends BaseController {
a1--;
}
}
processLoadedStickers(type, newStickerArray, false, (int) (System.currentTimeMillis() / 1000), res.hash);
processLoadedStickers(type, newStickerArray, false, (int) (System.currentTimeMillis() / 1000), res.hash2);
}
}));
}
@ -2140,7 +2145,7 @@ public class MediaDataController extends BaseController {
MediaDataController.getInstance(currentAccount).loadStickersByEmojiOrName(packName, false, true);
}
}
if (loadingGenericAnimations /*|| System.currentTimeMillis() - getUserConfig().lastUpdatedGenericAnimations < 86400000*/) {
if (loadingGenericAnimations || System.currentTimeMillis() - getUserConfig().lastUpdatedGenericAnimations < 86400000) {
return;
}
loadingGenericAnimations = true;
@ -2162,6 +2167,39 @@ public class MediaDataController extends BaseController {
}));
}
public void checkDefaultTopicIcons() {
if (getUserConfig().defaultTopicIcons != null) {
String packName = getUserConfig().defaultTopicIcons;
TLRPC.TL_messages_stickerSet set = getStickerSetByName(packName);
if (set == null) {
set = getStickerSetByEmojiOrName(packName);
}
if (set == null) {
MediaDataController.getInstance(currentAccount).loadStickersByEmojiOrName(packName, false, true);
}
}
if (loadingDefaultTopicIcons || System.currentTimeMillis() - getUserConfig().lastUpdatedDefaultTopicIcons < 86400000) {
return;
}
loadingDefaultTopicIcons = true;
TLRPC.TL_messages_getStickerSet req = new TLRPC.TL_messages_getStickerSet();
req.stickerset = new TLRPC.TL_inputStickerSetEmojiDefaultTopicIcons();
getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> {
if (response instanceof TLRPC.TL_messages_stickerSet) {
TLRPC.TL_messages_stickerSet stickerSet = (TLRPC.TL_messages_stickerSet) response;
getUserConfig().defaultTopicIcons = stickerSet.set.short_name;
getUserConfig().lastUpdatedDefaultTopicIcons = System.currentTimeMillis();
getUserConfig().saveConfig(false);
processLoadedDiceStickers(getUserConfig().defaultTopicIcons, false, stickerSet, false, (int) (System.currentTimeMillis() / 1000));
for (int i = 0; i < stickerSet.documents.size(); i++) {
preloadImage(ImageLocation.getForDocument(stickerSet.documents.get(i)), null);
}
}
}));
}
public void loadStickersByEmojiOrName(String name, boolean isEmoji, boolean cache) {
if (loadingDiceStickerSets.contains(name) || isEmoji && diceStickerSetsByEmoji.get(name) != null) {
return;
@ -2366,7 +2404,7 @@ public class MediaDataController extends BaseController {
if (type == TYPE_FEATURED || type == TYPE_FEATURED_EMOJIPACKS) {
final boolean emoji = type == TYPE_FEATURED_EMOJIPACKS;
TLRPC.TL_messages_allStickers response = new TLRPC.TL_messages_allStickers();
response.hash = loadFeaturedHash[emoji ? 1 : 0];
response.hash2 = loadFeaturedHash[emoji ? 1 : 0];
for (int a = 0, size = featuredStickerSets[emoji ? 1 : 0].size(); a < size; a++) {
response.sets.add(featuredStickerSets[emoji ? 1 : 0].get(a).set);
}
@ -2482,9 +2520,12 @@ public class MediaDataController extends BaseController {
}
public static long getStickerSetId(TLRPC.Document document) {
if (document == null) {
return -1;
}
for (int a = 0; a < document.attributes.size(); a++) {
TLRPC.DocumentAttribute attribute = document.attributes.get(a);
if (attribute instanceof TLRPC.TL_documentAttributeSticker) {
if (attribute instanceof TLRPC.TL_documentAttributeSticker || attribute instanceof TLRPC.TL_documentAttributeCustomEmoji) {
if (attribute.stickerset instanceof TLRPC.TL_inputStickerSetID) {
return attribute.stickerset.id;
}
@ -3142,6 +3183,10 @@ public class MediaDataController extends BaseController {
req.from_id = MessagesController.getInputPeer(chat);
req.flags |= 1;
}
if (replyMessageId != 0) {
req.top_msg_id = replyMessageId;
req.flags |= 2;
}
req.filter = new TLRPC.TL_inputMessagesFilterEmpty();
mergeReqId = getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> {
if (lastMergeDialogId == mergeDialogId) {
@ -3281,14 +3326,14 @@ public class MediaDataController extends BaseController {
public final static int MEDIA_TYPES_COUNT = 8;
public void loadMedia(long dialogId, int count, int max_id, int min_id, int type, int fromCache, int classGuid, int requestIndex) {
public void loadMedia(long dialogId, int count, int max_id, int min_id, int type, int topicId, int fromCache, int classGuid, int requestIndex) {
boolean isChannel = DialogObject.isChatDialog(dialogId) && ChatObject.isChannel(-dialogId, currentAccount);
if (BuildVars.LOGS_ENABLED) {
FileLog.d("load media did " + dialogId + " count = " + count + " max_id " + max_id + " type = " + type + " cache = " + fromCache + " classGuid = " + classGuid);
}
if ((fromCache != 0 || DialogObject.isEncryptedDialog(dialogId))) {
loadMediaDatabase(dialogId, count, max_id, min_id, type, classGuid, isChannel, fromCache, requestIndex);
if (fromCache != 0 || DialogObject.isEncryptedDialog(dialogId)) {
loadMediaDatabase(dialogId, count, max_id, min_id, type, topicId, classGuid, isChannel, fromCache, requestIndex);
} else {
TLRPC.TL_messages_search req = new TLRPC.TL_messages_search();
req.limit = count;
@ -3318,6 +3363,10 @@ public class MediaDataController extends BaseController {
}
req.q = "";
req.peer = getMessagesController().getInputPeer(dialogId);
if (topicId != 0) {
req.top_msg_id = topicId;
req.flags |= 2;
}
if (req.peer == null) {
return;
}
@ -3332,20 +3381,25 @@ public class MediaDataController extends BaseController {
topReached = res.messages.size() == 0;
}
processLoadedMedia(res, dialogId, count, max_id, min_id, type, 0, classGuid, isChannel, topReached, requestIndex);
processLoadedMedia(res, dialogId, count, max_id, min_id, type, topicId, 0, classGuid, isChannel, topReached, requestIndex);
}
});
getConnectionsManager().bindRequestToGuid(reqId, classGuid);
}
}
public void getMediaCounts(long dialogId, int classGuid) {
public void getMediaCounts(long dialogId, int topicId, int classGuid) {
getMessagesStorage().getStorageQueue().postRunnable(() -> {
try {
int[] counts = new int[]{-1, -1, -1, -1, -1, -1, -1, -1};
int[] countsFinal = new int[]{-1, -1, -1, -1, -1, -1, -1, -1};
int[] old = new int[]{0, 0, 0, 0, 0, 0, 0, 0};
SQLiteCursor cursor = getMessagesStorage().getDatabase().queryFinalized(String.format(Locale.US, "SELECT type, count, old FROM media_counts_v2 WHERE uid = %d", dialogId));
SQLiteCursor cursor;
if (topicId != 0) {
cursor = getMessagesStorage().getDatabase().queryFinalized(String.format(Locale.US, "SELECT type, count, old FROM media_counts_topics WHERE uid = %d AND topic_id = %d", dialogId, topicId));
} else {
cursor = getMessagesStorage().getDatabase().queryFinalized(String.format(Locale.US, "SELECT type, count, old FROM media_counts_v2 WHERE uid = %d", dialogId));
}
while (cursor.next()) {
int type = cursor.intValue(0);
if (type >= 0 && type < MEDIA_TYPES_COUNT) {
@ -3364,14 +3418,18 @@ public class MediaDataController extends BaseController {
counts[a] = 0;
}
cursor.dispose();
putMediaCountDatabase(dialogId, a, counts[a]);
putMediaCountDatabase(dialogId, topicId, a, counts[a]);
}
}
AndroidUtilities.runOnUIThread(() -> getNotificationCenter().postNotificationName(NotificationCenter.mediaCountsDidLoad, dialogId, counts));
AndroidUtilities.runOnUIThread(() -> getNotificationCenter().postNotificationName(NotificationCenter.mediaCountsDidLoad, dialogId, topicId, counts));
} else {
boolean missing = false;
TLRPC.TL_messages_getSearchCounters req = new TLRPC.TL_messages_getSearchCounters();
req.peer = getMessagesController().getInputPeer(dialogId);
if (topicId != 0) {
req.top_msg_id = topicId;
req.flags |= 1;
}
for (int a = 0; a < counts.length; a++) {
if (req.peer == null) {
counts[a] = 0;
@ -3434,15 +3492,15 @@ public class MediaDataController extends BaseController {
continue;
}
counts[type] = searchCounter.count;
putMediaCountDatabase(dialogId, type, counts[type]);
putMediaCountDatabase(dialogId, topicId, type, counts[type]);
}
}
AndroidUtilities.runOnUIThread(() -> getNotificationCenter().postNotificationName(NotificationCenter.mediaCountsDidLoad, dialogId, counts));
AndroidUtilities.runOnUIThread(() -> getNotificationCenter().postNotificationName(NotificationCenter.mediaCountsDidLoad, dialogId, topicId, counts));
});
getConnectionsManager().bindRequestToGuid(reqId, classGuid);
}
if (!missing) {
AndroidUtilities.runOnUIThread(() -> getNotificationCenter().postNotificationName(NotificationCenter.mediaCountsDidLoad, dialogId, countsFinal));
AndroidUtilities.runOnUIThread(() -> getNotificationCenter().postNotificationName(NotificationCenter.mediaCountsDidLoad, dialogId, topicId, countsFinal));
}
}
} catch (Exception e) {
@ -3451,9 +3509,9 @@ public class MediaDataController extends BaseController {
});
}
public void getMediaCount(long dialogId, int type, int classGuid, boolean fromCache) {
public void getMediaCount(long dialogId, int topicId, int type, int classGuid, boolean fromCache) {
if (fromCache || DialogObject.isEncryptedDialog(dialogId)) {
getMediaCountDatabase(dialogId, type, classGuid);
getMediaCountDatabase(dialogId, topicId, type, classGuid);
} else {
TLRPC.TL_messages_getSearchCounters req = new TLRPC.TL_messages_getSearchCounters();
if (type == MEDIA_PHOTOVIDEO) {
@ -3469,6 +3527,10 @@ public class MediaDataController extends BaseController {
} else if (type == MEDIA_GIF) {
req.filters.add(new TLRPC.TL_inputMessagesFilterGif());
}
if (topicId != 0) {
req.top_msg_id = topicId;
req.flags |= 1;
}
req.peer = getMessagesController().getInputPeer(dialogId);
if (req.peer == null) {
return;
@ -3478,7 +3540,7 @@ public class MediaDataController extends BaseController {
TLRPC.Vector res = (TLRPC.Vector) response;
if (!res.objects.isEmpty()) {
TLRPC.TL_messages_searchCounter counter = (TLRPC.TL_messages_searchCounter) res.objects.get(0);
processLoadedMediaCount(counter.count, dialogId, type, classGuid, false, 0);
processLoadedMediaCount(counter.count, dialogId, topicId, type, classGuid, false, 0);
}
}
});
@ -3551,20 +3613,24 @@ public class MediaDataController extends BaseController {
}
}
private void processLoadedMedia(TLRPC.messages_Messages res, long dialogId, int count, int max_id, int min_id, int type, int fromCache, int classGuid, boolean isChannel, boolean topReached, int requestIndex) {
private void processLoadedMedia(TLRPC.messages_Messages res, long dialogId, int count, int max_id, int min_id, int type, int topicId, int fromCache, int classGuid, boolean isChannel, boolean topReached, int requestIndex) {
if (BuildVars.LOGS_ENABLED) {
FileLog.d("process load media did " + dialogId + " count = " + count + " max_id=" + max_id + " min_id=" + min_id + " type = " + type + " cache = " + fromCache + " classGuid = " + classGuid);
int messagesCount = 0;
if (res != null && res.messages != null) {
messagesCount = res.messages.size();
}
FileLog.d("process load media messagesCount " + messagesCount + " did " + dialogId + " topicId " + topicId + " count = " + count + " max_id=" + max_id + " min_id=" + min_id + " type = " + type + " cache = " + fromCache + " classGuid = " + classGuid);
}
if (fromCache != 0 && ((res.messages.isEmpty() && min_id == 0) || (res.messages.size() <= 1 && min_id != 0)) && !DialogObject.isEncryptedDialog(dialogId)) {
if (fromCache == 2) {
return;
}
loadMedia(dialogId, count, max_id, min_id, type, 0, classGuid, requestIndex);
loadMedia(dialogId, count, max_id, min_id, type, topicId, 0, classGuid, requestIndex);
} else {
if (fromCache == 0) {
ImageLoader.saveMessagesThumbs(res.messages);
getMessagesStorage().putUsersAndChats(res.users, res.chats, true, true);
putMediaDatabase(dialogId, type, res.messages, max_id, min_id, topReached);
putMediaDatabase(dialogId, topicId, type, res.messages, max_id, min_id, topReached);
}
Utilities.searchQueue.postRunnable(() -> {
@ -3592,31 +3658,40 @@ public class MediaDataController extends BaseController {
}
}
private void processLoadedMediaCount(int count, long dialogId, int type, int classGuid, boolean fromCache, int old) {
private void processLoadedMediaCount(int count, long dialogId, int topicId, int type, int classGuid, boolean fromCache, int old) {
AndroidUtilities.runOnUIThread(() -> {
boolean isEncryptedDialog = DialogObject.isEncryptedDialog(dialogId);
boolean reload = fromCache && (count == -1 || count == 0 && type == 2) && !isEncryptedDialog;
if (reload || old == 1 && !isEncryptedDialog) {
getMediaCount(dialogId, type, classGuid, false);
getMediaCount(dialogId, topicId, type, classGuid, false);
}
if (!reload) {
if (!fromCache) {
putMediaCountDatabase(dialogId, type, count);
putMediaCountDatabase(dialogId, topicId, type, count);
}
getNotificationCenter().postNotificationName(NotificationCenter.mediaCountDidLoad, dialogId, (fromCache && count == -1 ? 0 : count), fromCache, type);
getNotificationCenter().postNotificationName(NotificationCenter.mediaCountDidLoad, dialogId, topicId, (fromCache && count == -1 ? 0 : count), fromCache, type);
}
});
}
private void putMediaCountDatabase(long uid, int type, int count) {
private void putMediaCountDatabase(long uid, int topicId, int type, int count) {
getMessagesStorage().getStorageQueue().postRunnable(() -> {
try {
SQLitePreparedStatement state2 = getMessagesStorage().getDatabase().executeFast("REPLACE INTO media_counts_v2 VALUES(?, ?, ?, ?)");
SQLitePreparedStatement state2;
if (topicId != 0) {
state2 = getMessagesStorage().getDatabase().executeFast("REPLACE INTO media_counts_topics VALUES(?, ?, ?, ?, ?)");
} else {
state2 = getMessagesStorage().getDatabase().executeFast("REPLACE INTO media_counts_v2 VALUES(?, ?, ?, ?)");
}
int pointer = 1;
state2.requery();
state2.bindLong(1, uid);
state2.bindInteger(2, type);
state2.bindInteger(3, count);
state2.bindInteger(4, 0);
state2.bindLong(pointer++, uid);
if (topicId != 0) {
state2.bindInteger(pointer++, topicId);
}
state2.bindInteger(pointer++, type);
state2.bindInteger(pointer++, count);
state2.bindInteger(pointer++, 0);
state2.step();
state2.dispose();
} catch (Exception e) {
@ -3625,12 +3700,17 @@ public class MediaDataController extends BaseController {
});
}
private void getMediaCountDatabase(long dialogId, int type, int classGuid) {
private void getMediaCountDatabase(long dialogId, int topicId, int type, int classGuid) {
getMessagesStorage().getStorageQueue().postRunnable(() -> {
try {
int count = -1;
int old = 0;
SQLiteCursor cursor = getMessagesStorage().getDatabase().queryFinalized(String.format(Locale.US, "SELECT count, old FROM media_counts_v2 WHERE uid = %d AND type = %d LIMIT 1", dialogId, type));
SQLiteCursor cursor;
if (topicId != 0) {
cursor = getMessagesStorage().getDatabase().queryFinalized(String.format(Locale.US, "SELECT count, old FROM media_counts_topics WHERE uid = %d AND topic_id = %d AND type = %d LIMIT 1", dialogId, topicId, type));
} else {
cursor = getMessagesStorage().getDatabase().queryFinalized(String.format(Locale.US, "SELECT count, old FROM media_counts_v2 WHERE uid = %d AND type = %d LIMIT 1", dialogId, type));
}
if (cursor.next()) {
count = cursor.intValue(0);
old = cursor.intValue(1);
@ -3644,17 +3724,17 @@ public class MediaDataController extends BaseController {
cursor.dispose();
if (count != -1) {
putMediaCountDatabase(dialogId, type, count);
putMediaCountDatabase(dialogId, topicId, type, count);
}
}
processLoadedMediaCount(count, dialogId, type, classGuid, true, old);
processLoadedMediaCount(count, dialogId, topicId, type, classGuid, true, old);
} catch (Exception e) {
FileLog.e(e);
}
});
}
private void loadMediaDatabase(long uid, int count, int max_id, int min_id, int type, int classGuid, boolean isChannel, int fromCache, int requestIndex) {
private void loadMediaDatabase(long uid, int count, int max_id, int min_id, int type, int topicId, int classGuid, boolean isChannel, int fromCache, int requestIndex) {
Runnable runnable = new Runnable() {
@Override
public void run() {
@ -3669,23 +3749,41 @@ public class MediaDataController extends BaseController {
SQLiteDatabase database = getMessagesStorage().getDatabase();
boolean isEnd = false;
boolean reverseMessages = false;
if (!DialogObject.isEncryptedDialog(uid)) {
if (min_id == 0) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT start FROM media_holes_v2 WHERE uid = %d AND type = %d AND start IN (0, 1)", uid, type));
if (topicId != 0) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT start FROM media_holes_topics WHERE uid = %d AND topic_id = %d AND type = %d AND start IN (0, 1)", uid, topicId, type));
} else {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT start FROM media_holes_v2 WHERE uid = %d AND type = %d AND start IN (0, 1)", uid, type));
}
if (cursor.next()) {
isEnd = cursor.intValue(0) == 1;
} else {
cursor.dispose();
cursor = database.queryFinalized(String.format(Locale.US, "SELECT min(mid) FROM media_v4 WHERE uid = %d AND type = %d AND mid > 0", uid, type));
if (topicId != 0) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT min(mid) FROM media_topics WHERE uid = %d AND topic_id = %d AND type = %d AND mid > 0", uid, topicId, type));
} else {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT min(mid) FROM media_v4 WHERE uid = %d AND type = %d AND mid > 0", uid, type));
}
if (cursor.next()) {
int mid = cursor.intValue(0);
if (mid != 0) {
SQLitePreparedStatement state = database.executeFast("REPLACE INTO media_holes_v2 VALUES(?, ?, ?, ?)");
SQLitePreparedStatement state;
if (topicId != 0) {
state = database.executeFast("REPLACE INTO media_holes_topics VALUES(?, ?, ?, ?, ?)");
} else {
state = database.executeFast("REPLACE INTO media_holes_v2 VALUES(?, ?, ?, ?)");
}
int pointer = 1;
state.requery();
state.bindLong(1, uid);
state.bindInteger(2, type);
state.bindInteger(3, 0);
state.bindInteger(4, mid);
state.bindLong(pointer++, uid);
if (topicId != 0) {
state.bindInteger(pointer++, topicId);
}
state.bindInteger(pointer++, type);
state.bindInteger(pointer++, 0);
state.bindInteger(pointer++, mid);
state.step();
state.dispose();
}
@ -3697,57 +3795,106 @@ public class MediaDataController extends BaseController {
int holeMessageId = 0;
if (max_id != 0) {
int startHole = 0;
cursor = database.queryFinalized(String.format(Locale.US, "SELECT start, end FROM media_holes_v2 WHERE uid = %d AND type = %d AND start <= %d ORDER BY end DESC LIMIT 1", uid, type, max_id));
if (topicId != 0) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT start, end FROM media_holes_topics WHERE uid = %d AND topic_id = %d AND type = %d AND start <= %d ORDER BY end DESC LIMIT 1", uid, topicId, type, max_id));
} else {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT start, end FROM media_holes_v2 WHERE uid = %d AND type = %d AND start <= %d ORDER BY end DESC LIMIT 1", uid, type, max_id));
}
if (cursor.next()) {
startHole = cursor.intValue(0);
holeMessageId = cursor.intValue(1);
}
cursor.dispose();
if (holeMessageId > 1) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media_v4 WHERE uid = %d AND mid > 0 AND mid < %d AND mid >= %d AND type = %d ORDER BY date DESC, mid DESC LIMIT %d", uid, max_id, holeMessageId, type, countToLoad));
isEnd = false;
if (topicId != 0) {
if (holeMessageId > 1) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media_topics WHERE uid = %d AND topic_id = %d AND mid > 0 AND mid < %d AND mid >= %d AND type = %d ORDER BY date DESC, mid DESC LIMIT %d", uid, topicId, max_id, holeMessageId, type, countToLoad));
isEnd = false;
} else {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media_topics WHERE uid = %d AND topic_id = %d AND mid > 0 AND mid < %d AND type = %d ORDER BY date DESC, mid DESC LIMIT %d", uid, topicId, max_id, type, countToLoad));
}
} else {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media_v4 WHERE uid = %d AND mid > 0 AND mid < %d AND type = %d ORDER BY date DESC, mid DESC LIMIT %d", uid, max_id, type, countToLoad));
if (holeMessageId > 1) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media_v4 WHERE uid = %d AND mid > 0 AND mid < %d AND mid >= %d AND type = %d ORDER BY date DESC, mid DESC LIMIT %d", uid, max_id, holeMessageId, type, countToLoad));
isEnd = false;
} else {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media_v4 WHERE uid = %d AND mid > 0 AND mid < %d AND type = %d ORDER BY date DESC, mid DESC LIMIT %d", uid, max_id, type, countToLoad));
}
}
} else if (min_id != 0) {
int startHole = 0;
cursor = database.queryFinalized(String.format(Locale.US, "SELECT start, end FROM media_holes_v2 WHERE uid = %d AND type = %d AND end >= %d ORDER BY end ASC LIMIT 1", uid, type, min_id));
if (topicId != 0) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT start, end FROM media_holes_topics WHERE uid = %d AND topic_id = %d AND type = %d AND end >= %d ORDER BY end ASC LIMIT 1", uid, topicId, type, min_id));
} else {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT start, end FROM media_holes_v2 WHERE uid = %d AND type = %d AND end >= %d ORDER BY end ASC LIMIT 1", uid, type, min_id));
}
if (cursor.next()) {
startHole = cursor.intValue(0);
holeMessageId = cursor.intValue(1);
}
cursor.dispose();
reverseMessages = true;
if (startHole > 1) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media_v4 WHERE uid = %d AND mid > 0 AND mid >= %d AND mid <= %d AND type = %d ORDER BY date ASC, mid ASC LIMIT %d", uid, min_id, startHole, type, countToLoad));
if (topicId != 0) {
if (startHole > 1) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media_topics WHERE uid = %d AND topic_id = %d AND mid > 0 AND mid >= %d AND mid <= %d AND type = %d ORDER BY date ASC, mid ASC LIMIT %d", uid, topicId, min_id, startHole, type, countToLoad));
} else {
isEnd = true;
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media_topics WHERE uid = %d AND topic_id = %d AND mid > 0 AND mid >= %d AND type = %d ORDER BY date ASC, mid ASC LIMIT %d", uid, topicId, min_id, type, countToLoad));
}
} else {
isEnd = true;
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media_v4 WHERE uid = %d AND mid > 0 AND mid >= %d AND type = %d ORDER BY date ASC, mid ASC LIMIT %d", uid, min_id, type, countToLoad));
if (startHole > 1) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media_v4 WHERE uid = %d AND mid > 0 AND mid >= %d AND mid <= %d AND type = %d ORDER BY date ASC, mid ASC LIMIT %d", uid, min_id, startHole, type, countToLoad));
} else {
isEnd = true;
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media_v4 WHERE uid = %d AND mid > 0 AND mid >= %d AND type = %d ORDER BY date ASC, mid ASC LIMIT %d", uid, min_id, type, countToLoad));
}
}
} else {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT max(end) FROM media_holes_v2 WHERE uid = %d AND type = %d", uid, type));
if (topicId != 0) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT max(end) FROM media_holes_topics WHERE uid = %d AND topic_id = %d AND type = %d", uid, topicId, type));
} else {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT max(end) FROM media_holes_v2 WHERE uid = %d AND type = %d", uid, type));
}
if (cursor.next()) {
holeMessageId = cursor.intValue(0);
}
cursor.dispose();
if (holeMessageId > 1) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media_v4 WHERE uid = %d AND mid >= %d AND type = %d ORDER BY date DESC, mid DESC LIMIT %d", uid, holeMessageId, type, countToLoad));
if (topicId != 0) {
if (holeMessageId > 1) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media_topics WHERE uid = %d AND topic_id = %d AND mid >= %d AND type = %d ORDER BY date DESC, mid DESC LIMIT %d", uid, topicId, holeMessageId, type, countToLoad));
} else {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media_topics WHERE uid = %d AND topic_id = %d AND mid > 0 AND type = %d ORDER BY date DESC, mid DESC LIMIT %d", uid, topicId, type, countToLoad));
}
} else {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media_v4 WHERE uid = %d AND mid > 0 AND type = %d ORDER BY date DESC, mid DESC LIMIT %d", uid, type, countToLoad));
if (holeMessageId > 1) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media_v4 WHERE uid = %d AND mid >= %d AND type = %d ORDER BY date DESC, mid DESC LIMIT %d", uid, holeMessageId, type, countToLoad));
} else {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media_v4 WHERE uid = %d AND mid > 0 AND type = %d ORDER BY date DESC, mid DESC LIMIT %d", uid, type, countToLoad));
}
}
}
} else {
isEnd = true;
if (max_id != 0) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT m.data, m.mid, r.random_id FROM media_v4 as m LEFT JOIN randoms_v2 as r ON r.mid = m.mid WHERE m.uid = %d AND m.mid > %d AND type = %d ORDER BY m.mid ASC LIMIT %d", uid, max_id, type, countToLoad));
} else if (min_id != 0) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT m.data, m.mid, r.random_id FROM media_v4 as m LEFT JOIN randoms_v2 as r ON r.mid = m.mid WHERE m.uid = %d AND m.mid < %d AND type = %d ORDER BY m.mid DESC LIMIT %d", uid, min_id, type, countToLoad));
if (topicId != 0) {
if (max_id != 0) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT m.data, m.mid, r.random_id FROM media_topics as m LEFT JOIN randoms_v2 as r ON r.mid = m.mid WHERE m.uid = %d AND m.topic_id = %d AND m.mid > %d AND type = %d ORDER BY m.mid ASC LIMIT %d", uid, topicId, max_id, type, countToLoad));
} else if (min_id != 0) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT m.data, m.mid, r.random_id FROM media_topics as m LEFT JOIN randoms_v2 as r ON r.mid = m.mid WHERE m.uid = %d AND m.topic_id = %d AND m.mid < %d AND type = %d ORDER BY m.mid DESC LIMIT %d", uid, topicId, min_id, type, countToLoad));
} else {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT m.data, m.mid, r.random_id FROM media_topics as m LEFT JOIN randoms_v2 as r ON r.mid = m.mid WHERE m.uid = %d AND m.topic_id = %d AND type = %d ORDER BY m.mid ASC LIMIT %d", uid, topicId, type, countToLoad));
}
} else {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT m.data, m.mid, r.random_id FROM media_v4 as m LEFT JOIN randoms_v2 as r ON r.mid = m.mid WHERE m.uid = %d AND type = %d ORDER BY m.mid ASC LIMIT %d", uid, type, countToLoad));
if (max_id != 0) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT m.data, m.mid, r.random_id FROM media_v4 as m LEFT JOIN randoms_v2 as r ON r.mid = m.mid WHERE m.uid = %d AND m.mid > %d AND type = %d ORDER BY m.mid ASC LIMIT %d", uid, max_id, type, countToLoad));
} else if (min_id != 0) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT m.data, m.mid, r.random_id FROM media_v4 as m LEFT JOIN randoms_v2 as r ON r.mid = m.mid WHERE m.uid = %d AND m.mid < %d AND type = %d ORDER BY m.mid DESC LIMIT %d", uid, min_id, type, countToLoad));
} else {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT m.data, m.mid, r.random_id FROM media_v4 as m LEFT JOIN randoms_v2 as r ON r.mid = m.mid WHERE m.uid = %d AND type = %d ORDER BY m.mid ASC LIMIT %d", uid, type, countToLoad));
}
}
}
while (cursor.next()) {
NativeByteBuffer data = cursor.byteBufferValue(0);
if (data != null) {
@ -3793,7 +3940,7 @@ public class MediaDataController extends BaseController {
} finally {
Runnable task = this;
AndroidUtilities.runOnUIThread(() -> getMessagesStorage().completeTaskForGuid(task, classGuid));
processLoadedMedia(res, uid, count, max_id, min_id, type, fromCache, classGuid, isChannel, topReached, requestIndex);
processLoadedMedia(res, uid, count, max_id, min_id, type, topicId, fromCache, classGuid, isChannel, topReached, requestIndex);
}
}
};
@ -3802,27 +3949,37 @@ public class MediaDataController extends BaseController {
messagesStorage.bindTaskToGuid(runnable, classGuid);
}
private void putMediaDatabase(long uid, int type, ArrayList<TLRPC.Message> messages, int max_id, int min_id, boolean topReached) {
private void putMediaDatabase(long uid, int topicId, int type, ArrayList<TLRPC.Message> messages, int max_id, int min_id, boolean topReached) {
getMessagesStorage().getStorageQueue().postRunnable(() -> {
try {
if (min_id == 0 && (messages.isEmpty() || topReached)) {
getMessagesStorage().doneHolesInMedia(uid, max_id, type);
getMessagesStorage().doneHolesInMedia(uid, max_id, type, topicId);
if (messages.isEmpty()) {
return;
}
}
getMessagesStorage().getDatabase().beginTransaction();
SQLitePreparedStatement state2 = getMessagesStorage().getDatabase().executeFast("REPLACE INTO media_v4 VALUES(?, ?, ?, ?, ?)");
SQLitePreparedStatement state2;
if (topicId != 0) {
state2 = getMessagesStorage().getDatabase().executeFast("REPLACE INTO media_topics VALUES(?, ?, ?, ?, ?, ?)");
} else {
state2 = getMessagesStorage().getDatabase().executeFast("REPLACE INTO media_v4 VALUES(?, ?, ?, ?, ?)");
}
for (TLRPC.Message message : messages) {
if (canAddMessageToMedia(message)) {
state2.requery();
NativeByteBuffer data = new NativeByteBuffer(message.getObjectSize());
message.serializeToStream(data);
state2.bindInteger(1, message.id);
state2.bindLong(2, uid);
state2.bindInteger(3, message.date);
state2.bindInteger(4, type);
state2.bindByteBuffer(5, data);
int pointer = 1;
state2.bindInteger(pointer++, message.id);
state2.bindLong(pointer++, uid);
if (topicId != 0) {
state2.bindInteger(pointer++, topicId);
}
state2.bindInteger(pointer++, message.date);
state2.bindInteger(pointer++, type);
state2.bindByteBuffer(pointer++, data);
state2.step();
data.reuse();
}
@ -3831,11 +3988,11 @@ public class MediaDataController extends BaseController {
if (!topReached || max_id != 0 || min_id != 0) {
int minId = (topReached && min_id == 0) ? 1 : messages.get(messages.size() - 1).id;
if (min_id != 0) {
getMessagesStorage().closeHolesInMedia(uid, minId, messages.get(0).id, type);
getMessagesStorage().closeHolesInMedia(uid, minId, messages.get(0).id, type, topicId);
} else if (max_id != 0) {
getMessagesStorage().closeHolesInMedia(uid, minId, max_id, type);
getMessagesStorage().closeHolesInMedia(uid, minId, max_id, type, topicId);
} else {
getMessagesStorage().closeHolesInMedia(uid, minId, Integer.MAX_VALUE, type);
getMessagesStorage().closeHolesInMedia(uid, minId, Integer.MAX_VALUE, type, topicId);
}
}
getMessagesStorage().getDatabase().commitTransaction();
@ -4932,7 +5089,7 @@ public class MediaDataController extends BaseController {
}
}
public void loadReplyMessagesForMessages(ArrayList<MessageObject> messages, long dialogId, boolean scheduled, Runnable callback) {
public void loadReplyMessagesForMessages(ArrayList<MessageObject> messages, long dialogId, boolean scheduled, int threadMessageId, Runnable callback) {
if (DialogObject.isEncryptedDialog(dialogId)) {
ArrayList<Long> replyMessages = new ArrayList<>();
LongSparseArray<ArrayList<MessageObject>> replyMessageRandomOwners = new LongSparseArray<>();
@ -5020,6 +5177,9 @@ public class MediaDataController extends BaseController {
}
if (messageObject.getId() > 0 && messageObject.isReply()) {
int messageId = messageObject.messageOwner.reply_to.reply_to_msg_id;
if (messageId == threadMessageId) {
continue;
}
long channelId = 0;
if (messageObject.messageOwner.reply_to.reply_to_peer_id != null) {
if (messageObject.messageOwner.reply_to.reply_to_peer_id.channel_id != 0) {
@ -5209,10 +5369,12 @@ public class MediaDataController extends BaseController {
try {
getMessagesStorage().getDatabase().beginTransaction();
SQLitePreparedStatement state;
SQLitePreparedStatement topicState = null;
if (scheduled) {
state = getMessagesStorage().getDatabase().executeFast("UPDATE scheduled_messages_v2 SET replydata = ?, reply_to_message_id = ? WHERE mid = ? AND uid = ?");
} else {
state = getMessagesStorage().getDatabase().executeFast("UPDATE messages_v2 SET replydata = ?, reply_to_message_id = ? WHERE mid = ? AND uid = ?");
topicState = getMessagesStorage().getDatabase().executeFast("UPDATE messages_topics SET replydata = ?, reply_to_message_id = ? WHERE mid = ? AND uid = ?");
}
for (int a = 0; a < result.size(); a++) {
TLRPC.Message message = result.get(a);
@ -5227,17 +5389,26 @@ public class MediaDataController extends BaseController {
message.serializeToStream(data);
for (int b = 0; b < messageObjects.size(); b++) {
MessageObject messageObject = messageObjects.get(b);
state.requery();
state.bindByteBuffer(1, data);
state.bindInteger(2, message.id);
state.bindInteger(3, messageObject.getId());
state.bindLong(4, messageObject.getDialogId());
state.step();
for (int i = 0; i < 2; i++) {
SQLitePreparedStatement localState = i == 0 ? state : topicState;
if (localState == null) {
continue;
}
localState.requery();
localState.bindByteBuffer(1, data);
localState.bindInteger(2, message.id);
localState.bindInteger(3, messageObject.getId());
localState.bindLong(4, messageObject.getDialogId());
localState.step();
}
}
data.reuse();
}
}
state.dispose();
if (topicState != null) {
topicState.dispose();
}
getMessagesStorage().getDatabase().commitTransaction();
} catch (Exception e) {
FileLog.e(e);
@ -5931,13 +6102,17 @@ public class MediaDataController extends BaseController {
}
saveDraft(dialogId, threadId, draftMessage, replyToMessage, false);
if (threadId == 0) {
if (threadId == 0 || ChatObject.isForum(currentAccount, dialogId)) {
if (!DialogObject.isEncryptedDialog(dialogId)) {
TLRPC.TL_messages_saveDraft req = new TLRPC.TL_messages_saveDraft();
req.peer = getMessagesController().getInputPeer(dialogId);
if (req.peer == null) {
return;
}
if (threadId != 0) {
req.top_msg_id = threadId;
}
req.message = draftMessage.message;
req.no_webpage = draftMessage.no_webpage;
req.reply_to_msg_id = draftMessage.reply_to_msg_id;
@ -6025,7 +6200,7 @@ public class MediaDataController extends BaseController {
serializedData.cleanup();
}
editor.commit();
if (fromServer && threadId == 0) {
if (fromServer && (threadId == 0 || getMessagesController().isForum(dialogId))) {
if (draft != null && draft.reply_to_msg_id != 0 && replyToMessage == null) {
TLRPC.User user = null;
TLRPC.Chat chat = null;
@ -7056,7 +7231,9 @@ public class MediaDataController extends BaseController {
@Override
public void run() {
for (int i = 0; i < previewItems.size(); i++) {
previewItems.get(i).chatTheme.loadPreviewColors(0);
if (previewItems.get(i).chatTheme != null) {
previewItems.get(i).chatTheme.loadPreviewColors(0);
}
}
AndroidUtilities.runOnUIThread(() -> {
defaultEmojiThemes.clear();

View File

@ -12,6 +12,7 @@ public class MessageCustomParamsHelper {
!message.voiceTranscriptionOpen &&
!message.voiceTranscriptionFinal &&
!message.voiceTranscriptionRated &&
!message.voiceTranscriptionForce &&
message.voiceTranscriptionId == 0 &&
!message.premiumEffectWasPlayed;
}
@ -20,6 +21,7 @@ public class MessageCustomParamsHelper {
toMessage.voiceTranscription = fromMessage.voiceTranscription;
toMessage.voiceTranscriptionOpen = fromMessage.voiceTranscriptionOpen;
toMessage.voiceTranscriptionFinal = fromMessage.voiceTranscriptionFinal;
toMessage.voiceTranscriptionForce = fromMessage.voiceTranscriptionForce;
toMessage.voiceTranscriptionRated = fromMessage.voiceTranscriptionRated;
toMessage.voiceTranscriptionId = fromMessage.voiceTranscriptionId;
toMessage.premiumEffectWasPlayed = fromMessage.premiumEffectWasPlayed;
@ -66,11 +68,13 @@ public class MessageCustomParamsHelper {
private Params_v1(TLRPC.Message message) {
this.message = message;
flags += message.voiceTranscription != null ? 1 : 0;
flags += message.voiceTranscriptionForce ? 2 : 0;
}
@Override
public void serializeToStream(AbstractSerializedData stream) {
stream.writeInt32(VERSION);
flags = message.voiceTranscriptionForce ? (flags | 2) : (flags &~ 2);
stream.writeInt32(flags);
if ((flags & 1) != 0) {
stream.writeString(message.voiceTranscription);
@ -89,6 +93,7 @@ public class MessageCustomParamsHelper {
if ((flags & 1) != 0) {
message.voiceTranscription = stream.readString(exception);
}
message.voiceTranscriptionForce = (flags & 2) != 0;
message.voiceTranscriptionOpen = stream.readBool(exception);
message.voiceTranscriptionFinal = stream.readBool(exception);
message.voiceTranscriptionRated = stream.readBool(exception);

View File

@ -8,8 +8,6 @@
package org.telegram.messenger;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
@ -30,6 +28,7 @@ import android.text.style.URLSpan;
import android.text.util.Linkify;
import android.util.Base64;
import androidx.annotation.IntDef;
import androidx.collection.LongSparseArray;
import org.telegram.PhoneFormat.PhoneFormat;
@ -43,6 +42,9 @@ import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Cells.ChatMessageCell;
import org.telegram.ui.Components.AnimatedEmojiDrawable;
import org.telegram.ui.Components.AnimatedEmojiSpan;
import org.telegram.ui.Components.EmojiView;
import org.telegram.ui.Components.Forum.ForumBubbleDrawable;
import org.telegram.ui.Components.Forum.ForumUtilities;
import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble;
import org.telegram.ui.Components.Reactions.ReactionsUtils;
import org.telegram.ui.Components.TextStyleSpan;
@ -60,6 +62,8 @@ import org.telegram.ui.Components.spoilers.SpoilerEffect;
import java.io.BufferedReader;
import java.io.File;
import java.io.StringReader;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.URLEncoder;
import java.util.AbstractMap;
import java.util.ArrayList;
@ -80,13 +84,20 @@ public class MessageObject {
public static final int MESSAGE_SEND_STATE_SEND_ERROR = 2;
public static final int MESSAGE_SEND_STATE_EDITING = 3;
public static final int TYPE_TEXT = 0;
public static final int TYPE_PHOTO = 1;
public static final int TYPE_VOICE = 2;
public static final int TYPE_VIDEO = 3;
public static final int TYPE_GEO = 4; // TL_messageMediaGeo, TL_messageMediaVenue, TL_messageMediaGeoLive
public static final int TYPE_ROUND_VIDEO = 5;
public static final int TYPE_GIF = 8;
public static final int TYPE_FILE = 9;
public static final int TYPE_ACTION_PHOTO = 11;
public static final int TYPE_CONTACT = 12;
public static final int TYPE_STICKER = 13;
public static final int TYPE_MUSIC = 14;
public static final int TYPE_ANIMATED_STICKER = 15;
public static final int TYPE_PHONE_CALL = 16;
public static final int TYPE_POLL = 17;
public static final int TYPE_GIFT_PREMIUM = 18;
public static final int TYPE_EMOJIS = 19;
@ -104,9 +115,12 @@ public class MessageObject {
public TLRPC.Message messageOwner;
public TLRPC.Document emojiAnimatedSticker;
public Long emojiAnimatedStickerId;
public boolean isTopicMainMessage;
private boolean emojiAnimatedStickerLoading;
public String emojiAnimatedStickerColor;
public CharSequence messageText;
public CharSequence messageTextShort;
public CharSequence messageTextForReply;
public CharSequence linkDescription;
public CharSequence caption;
public CharSequence youtubeDescription;
@ -158,6 +172,8 @@ public class MessageObject {
public int sponsoredChannelPost;
public TLRPC.ChatInvite sponsoredChatInvite;
public String sponsoredChatInviteHash;
public boolean sponsoredShowPeerPhoto;
public boolean sponsoredRecommended;
public String botStartParam;
@ -231,6 +247,7 @@ public class MessageObject {
// forwarding preview params
public boolean hideSendersName;
public TLRPC.Peer sendAsPeer;
public ForumBubbleDrawable[] topicIconDrawable = new ForumBubbleDrawable[1];
static final String[] excludeWords = new String[]{
" vs. ",
@ -250,6 +267,8 @@ public class MessageObject {
};
public Drawable customAvatarDrawable;
private byte[] randomWaveform;
public static boolean hasUnreadReactions(TLRPC.Message message) {
if (message == null) {
return false;
@ -281,6 +300,36 @@ public class MessageObject {
return false;
}
public static int getTopicId(TLRPC.Message message) {
if (message != null && message.action instanceof TLRPC.TL_messageActionTopicCreate) {
return message.id;
}
if (message == null || message.reply_to == null || !message.reply_to.forum_topic) {
return 0;
}
if (message instanceof TLRPC.TL_messageService && !(message.action instanceof TLRPC.TL_messageActionPinMessage)) {
int topicId = message.reply_to.reply_to_msg_id;
if (topicId == 0) {
topicId = message.reply_to.reply_to_top_id;
}
return topicId;
} else {
int topicId = message.reply_to.reply_to_top_id;
if (topicId == 0) {
topicId = message.reply_to.reply_to_msg_id;
}
return topicId;
}
}
public static boolean isTopicActionMessage(MessageObject message) {
if (message == null || message.messageOwner == null) {
return false;
}
return message.messageOwner.action instanceof TLRPC.TL_messageActionTopicCreate ||
message.messageOwner.action instanceof TLRPC.TL_messageActionTopicEdit;
}
public int getEmojiOnlyCount() {
return emojiOnlyCount;
}
@ -315,7 +364,7 @@ public class MessageObject {
}
}
if (changed) {
MessagesStorage.getInstance(currentAccount).markMessageReactionsAsRead(messageOwner.dialog_id, messageOwner.id, true);
MessagesStorage.getInstance(currentAccount).markMessageReactionsAsRead(messageOwner.dialog_id, getTopicId(messageOwner), messageOwner.id, true);
}
}
@ -1190,13 +1239,12 @@ public class MessageObject {
}
if (emojiAnimatedSticker == null && emojiAnimatedStickerId == null) {
generateLayout(null);
} else if (isSticker()) {
type = TYPE_STICKER;
} else if (isAnimatedSticker()) {
type = TYPE_ANIMATED_STICKER;
} else {
type = 1000;
if (isSticker()) {
type = TYPE_STICKER;
} else if (isAnimatedSticker()) {
type = TYPE_ANIMATED_STICKER;
}
}
}
@ -2153,6 +2201,112 @@ public class MessageObject {
spannableStringBuilder.replace(i, i + "**new**".length(), newReactions);
}
messageText = spannableStringBuilder;
} else if (event.action instanceof TLRPC.TL_channelAdminLogEventActionChangeUsernames) {
TLRPC.TL_channelAdminLogEventActionChangeUsernames log = (TLRPC.TL_channelAdminLogEventActionChangeUsernames) event.action;
ArrayList<String> oldUsernames = log.prev_value;
ArrayList<String> newUsernames = log.new_value;
messageText = null;
if (oldUsernames != null && newUsernames != null) {
if (newUsernames.size() + 1 == oldUsernames.size()) {
String removed = null;
for (int i = 0; i < oldUsernames.size(); ++i) {
String username = oldUsernames.get(i);
if (!newUsernames.contains(username)) {
if (removed == null) {
removed = username;
} else {
removed = null;
break;
}
}
}
if (removed != null) {
messageText = replaceWithLink(
LocaleController.formatString("EventLogDeactivatedUsername", R.string.EventLogDeactivatedUsername, "@" + removed),
"un1", fromUser
);
}
} else if (oldUsernames.size() + 1 == newUsernames.size()) {
String added = null;
for (int i = 0; i < newUsernames.size(); ++i) {
String username = newUsernames.get(i);
if (!oldUsernames.contains(username)) {
if (added == null) {
added = username;
} else {
added = null;
break;
}
}
}
if (added != null) {
messageText = replaceWithLink(
LocaleController.formatString("EventLogActivatedUsername", R.string.EventLogActivatedUsername, "@" + added),
"un1", fromUser
);
}
}
}
if (messageText == null) {
messageText = replaceWithLink(
LocaleController.formatString("EventLogChangeUsernames", R.string.EventLogChangeUsernames, getUsernamesString(oldUsernames), getUsernamesString(newUsernames)),
"un1", fromUser
);
}
} else if (event.action instanceof TLRPC.TL_channelAdminLogEventActionToggleForum) {
TLRPC.TL_channelAdminLogEventActionToggleForum toggleForum = (TLRPC.TL_channelAdminLogEventActionToggleForum) event.action;
if (toggleForum.new_value) {
messageText = replaceWithLink(
LocaleController.formatString("EventLogSwitchToForum", R.string.EventLogSwitchToForum),
"un1", fromUser
);
} else {
messageText = replaceWithLink(
LocaleController.formatString("EventLogSwitchToGroup", R.string.EventLogSwitchToGroup),
"un1", fromUser
);
}
} else if (event.action instanceof TLRPC.TL_channelAdminLogEventActionCreateTopic) {
TLRPC.TL_channelAdminLogEventActionCreateTopic createTopic = (TLRPC.TL_channelAdminLogEventActionCreateTopic) event.action;
messageText = replaceWithLink(
LocaleController.formatString("EventLogCreateTopic", R.string.EventLogCreateTopic),
"un1", fromUser
);
messageText = replaceWithLink(messageText, "un2", createTopic.topic);
} else if (event.action instanceof TLRPC.TL_channelAdminLogEventActionEditTopic) {
TLRPC.TL_channelAdminLogEventActionEditTopic editTopic = (TLRPC.TL_channelAdminLogEventActionEditTopic) event.action;
messageText = replaceWithLink(
LocaleController.formatString("EventLogEditTopic", R.string.EventLogEditTopic),
"un1", fromUser
);
messageText = replaceWithLink(messageText, "un2", editTopic.prev_topic);
messageText = replaceWithLink(messageText, "un3", editTopic.new_topic);
} else if (event.action instanceof TLRPC.TL_channelAdminLogEventActionDeleteTopic) {
TLRPC.TL_channelAdminLogEventActionDeleteTopic deleteTopic = (TLRPC.TL_channelAdminLogEventActionDeleteTopic) event.action;
messageText = replaceWithLink(
LocaleController.getString("EventLogDeleteTopic", R.string.EventLogDeleteTopic),
"un1", fromUser
);
messageText = replaceWithLink(messageText, "un2", deleteTopic.topic);
} else if (event.action instanceof TLRPC.TL_channelAdminLogEventActionPinTopic) {
TLRPC.TL_channelAdminLogEventActionPinTopic pinTopic = (TLRPC.TL_channelAdminLogEventActionPinTopic) event.action;
if (pinTopic.new_topic instanceof TLRPC.TL_forumTopic && ((TLRPC.TL_forumTopic)pinTopic.new_topic).pinned) {
messageText = replaceWithLink(
LocaleController.formatString("EventLogPinTopic", R.string.EventLogPinTopic),
"un1", fromUser
);
messageText = replaceWithLink(messageText, "un2", pinTopic.new_topic);
} else {
messageText = replaceWithLink(
LocaleController.formatString("EventLogUnpinTopic", R.string.EventLogUnpinTopic),
"un1", fromUser
);
messageText = replaceWithLink(messageText, "un2", pinTopic.new_topic);
}
} else {
messageText = "unsupported " + event.action;
}
@ -2266,6 +2420,21 @@ public class MessageObject {
return LocaleController.getString("NoReactions", R.string.NoReactions);
}
private String getUsernamesString(ArrayList<String> usernames) {
if (usernames == null || usernames.size() == 0) {
return LocaleController.getString("UsernameEmpty", R.string.UsernameEmpty).toLowerCase();
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < usernames.size(); ++i) {
sb.append("@");
sb.append(usernames.get(i));
if (i < usernames.size() - 1) {
sb.append(", ");
}
}
return sb.toString();
}
private String getUserName(TLObject object, ArrayList<TLRPC.MessageEntity> entities, int offset) {
String name;
String username;
@ -2286,7 +2455,7 @@ public class MessageObject {
} else {
TLRPC.Chat chat = (TLRPC.Chat) object;
name = chat.title;
username = chat.username;
username = ChatObject.getPublicUsername(chat);
id = -chat.id;
}
if (offset >= 0) {
@ -2355,8 +2524,8 @@ public class MessageObject {
fromUser = MessagesController.getInstance(currentAccount).getUser(messageOwner.from_id.user_id);
}
TLRPC.TL_game game = null;
if (replyMessageObject != null && replyMessageObject.getMedia(messageOwner) != null && replyMessageObject.getMedia(messageOwner).game != null) {
game = replyMessageObject.getMedia(messageOwner).game;
if (replyMessageObject != null && getMedia(replyMessageObject) != null && getMedia(replyMessageObject).game != null) {
game = getMedia(replyMessageObject).game;
}
if (game == null) {
if (fromUser != null && fromUser.id == UserConfig.getInstance(currentAccount).getClientUserId()) {
@ -2395,11 +2564,11 @@ public class MessageObject {
currency = "<error>";
FileLog.e(e);
}
if (replyMessageObject != null && replyMessageObject.getMedia(messageOwner) instanceof TLRPC.TL_messageMediaInvoice) {
if (replyMessageObject != null && getMedia(replyMessageObject) instanceof TLRPC.TL_messageMediaInvoice) {
if (messageOwner.action.recurring_init) {
messageText = LocaleController.formatString(R.string.PaymentSuccessfullyPaidRecurrent, currency, name, replyMessageObject.getMedia(messageOwner).title);
messageText = LocaleController.formatString(R.string.PaymentSuccessfullyPaidRecurrent, currency, name, getMedia(replyMessageObject).title);
} else {
messageText = LocaleController.formatString("PaymentSuccessfullyPaid", R.string.PaymentSuccessfullyPaid, currency, name, replyMessageObject.getMedia(messageOwner).title);
messageText = LocaleController.formatString("PaymentSuccessfullyPaid", R.string.PaymentSuccessfullyPaid, currency, name, getMedia(replyMessageObject).title);
}
} else {
if (messageOwner.action.recurring_init) {
@ -2438,24 +2607,24 @@ public class MessageObject {
messageText = replaceWithLink(LocaleController.getString("ActionPinnedRound", R.string.ActionPinnedRound), "un1", fromUser != null ? fromUser : chat);
} else if ((replyMessageObject.isSticker() || replyMessageObject.isAnimatedSticker()) && !replyMessageObject.isAnimatedEmoji()) {
messageText = replaceWithLink(LocaleController.getString("ActionPinnedSticker", R.string.ActionPinnedSticker), "un1", fromUser != null ? fromUser : chat);
} else if (replyMessageObject.getMedia(messageOwner) instanceof TLRPC.TL_messageMediaDocument) {
} else if (getMedia(replyMessageObject) instanceof TLRPC.TL_messageMediaDocument) {
messageText = replaceWithLink(LocaleController.getString("ActionPinnedFile", R.string.ActionPinnedFile), "un1", fromUser != null ? fromUser : chat);
} else if (replyMessageObject.getMedia(messageOwner) instanceof TLRPC.TL_messageMediaGeo) {
} else if (getMedia(replyMessageObject) instanceof TLRPC.TL_messageMediaGeo) {
messageText = replaceWithLink(LocaleController.getString("ActionPinnedGeo", R.string.ActionPinnedGeo), "un1", fromUser != null ? fromUser : chat);
} else if (replyMessageObject.getMedia(messageOwner) instanceof TLRPC.TL_messageMediaGeoLive) {
} else if (getMedia(replyMessageObject) instanceof TLRPC.TL_messageMediaGeoLive) {
messageText = replaceWithLink(LocaleController.getString("ActionPinnedGeoLive", R.string.ActionPinnedGeoLive), "un1", fromUser != null ? fromUser : chat);
} else if (replyMessageObject.getMedia(messageOwner) instanceof TLRPC.TL_messageMediaContact) {
} else if (getMedia(replyMessageObject) instanceof TLRPC.TL_messageMediaContact) {
messageText = replaceWithLink(LocaleController.getString("ActionPinnedContact", R.string.ActionPinnedContact), "un1", fromUser != null ? fromUser : chat);
} else if (replyMessageObject.getMedia(messageOwner) instanceof TLRPC.TL_messageMediaPoll) {
if (((TLRPC.TL_messageMediaPoll) replyMessageObject.getMedia(messageOwner)).poll.quiz) {
} else if (getMedia(replyMessageObject) instanceof TLRPC.TL_messageMediaPoll) {
if (((TLRPC.TL_messageMediaPoll) getMedia(replyMessageObject)).poll.quiz) {
messageText = replaceWithLink(LocaleController.getString("ActionPinnedQuiz", R.string.ActionPinnedQuiz), "un1", fromUser != null ? fromUser : chat);
} else {
messageText = replaceWithLink(LocaleController.getString("ActionPinnedPoll", R.string.ActionPinnedPoll), "un1", fromUser != null ? fromUser : chat);
}
} else if (replyMessageObject.getMedia(messageOwner) instanceof TLRPC.TL_messageMediaPhoto) {
} else if (getMedia(replyMessageObject) instanceof TLRPC.TL_messageMediaPhoto) {
messageText = replaceWithLink(LocaleController.getString("ActionPinnedPhoto", R.string.ActionPinnedPhoto), "un1", fromUser != null ? fromUser : chat);
} else if (replyMessageObject.getMedia(messageOwner) instanceof TLRPC.TL_messageMediaGame) {
messageText = replaceWithLink(LocaleController.formatString("ActionPinnedGame", R.string.ActionPinnedGame, "\uD83C\uDFAE " + replyMessageObject.getMedia(messageOwner).game.title), "un1", fromUser != null ? fromUser : chat);
} else if (getMedia(replyMessageObject) instanceof TLRPC.TL_messageMediaGame) {
messageText = replaceWithLink(LocaleController.formatString("ActionPinnedGame", R.string.ActionPinnedGame, "\uD83C\uDFAE " + getMedia(replyMessageObject).game.title), "un1", fromUser != null ? fromUser : chat);
messageText = Emoji.replaceEmoji(messageText, Theme.chat_msgTextPaint.getFontMetricsInt(), AndroidUtilities.dp(20), false);
} else if (replyMessageObject.messageText != null && replyMessageObject.messageText.length() > 0) {
CharSequence mess = AnimatedEmojiSpan.cloneSpans(replyMessageObject.messageText);
@ -3256,6 +3425,68 @@ public class MessageObject {
generatePinMessageText(fromUser, chat);
} else if (messageOwner.action instanceof TLRPC.TL_messageActionHistoryClear) {
messageText = LocaleController.getString("HistoryCleared", R.string.HistoryCleared);
} else if (messageOwner.action instanceof TLRPC.TL_messageActionTopicCreate) {
messageText = LocaleController.getString("TopicCreated", R.string.TopicCreated);
TLRPC.TL_messageActionTopicCreate createAction = (TLRPC.TL_messageActionTopicCreate) messageOwner.action;
TLRPC.TL_forumTopic forumTopic = new TLRPC.TL_forumTopic();
forumTopic.icon_emoji_id = createAction.icon_emoji_id;
forumTopic.title = createAction.title;
forumTopic.icon_color = createAction.icon_color;
messageTextShort = AndroidUtilities.replaceCharSequence("%s", LocaleController.getString("TopicWasCreatedAction", R.string.TopicWasCreatedAction), ForumUtilities.getTopicSpannedName(forumTopic, null));
} else if (messageOwner.action instanceof TLRPC.TL_messageActionTopicEdit) {
TLRPC.TL_messageActionTopicEdit editAction = (TLRPC.TL_messageActionTopicEdit) messageOwner.action;
String name = null;
if (fromUser != null) {
name = ContactsController.formatName(fromUser.first_name, fromUser.last_name);
} else if (fromChat != null) {
name = fromChat.title;
}
if (name != null) {
name = name.trim();
} else {
name = "DELETED";
}
if ((messageOwner.action.flags & 4) > 0) {
if (((TLRPC.TL_messageActionTopicEdit) messageOwner.action).closed) {
messageText = LocaleController.formatString("TopicClosed2", R.string.TopicClosed2, name);
messageTextShort = LocaleController.getString("TopicClosed", R.string.TopicClosed);
} else {
messageText = LocaleController.formatString("TopicRestarted2", R.string.TopicRestarted2, name);
messageTextShort = LocaleController.getString("TopicRestarted", R.string.TopicRestarted);
}
} else {
if ((messageOwner.action.flags & 2) != 0 && (messageOwner.action.flags & 1) != 0) {
TLRPC.TL_forumTopic forumTopic = new TLRPC.TL_forumTopic();
forumTopic.icon_emoji_id = editAction.icon_emoji_id;
forumTopic.title = editAction.title;
forumTopic.icon_color = ForumBubbleDrawable.serverSupportedColor[0];
CharSequence topicName = ForumUtilities.getTopicSpannedName(forumTopic, null, topicIconDrawable);
CharSequence str = AndroidUtilities.replaceCharSequence("%1$s", LocaleController.getString("TopicChangeIconAndTitleTo", R.string.TopicChangeIconAndTitleTo), name);
messageText = AndroidUtilities.replaceCharSequence("%2$s", str, topicName);
messageTextShort = LocaleController.getString("TopicRenamed", R.string.TopicRenamed);
messageTextForReply = AndroidUtilities.replaceCharSequence("%s", LocaleController.getString("TopicChangeIconAndTitleToInReply", R.string.TopicChangeIconAndTitleToInReply), topicName);
} else if ((messageOwner.action.flags & 2) != 0) {
TLRPC.TL_forumTopic forumTopic = new TLRPC.TL_forumTopic();
forumTopic.icon_emoji_id = editAction.icon_emoji_id;
forumTopic.title = "";
forumTopic.icon_color = ForumBubbleDrawable.serverSupportedColor[0];
CharSequence topicName = ForumUtilities.getTopicSpannedName(forumTopic, null, topicIconDrawable);
CharSequence str = AndroidUtilities.replaceCharSequence("%1$s", LocaleController.getString("TopicIconChangedTo", R.string.TopicIconChangedTo), name);
messageText = AndroidUtilities.replaceCharSequence("%2$s", str, topicName);
messageTextShort = LocaleController.getString("TopicIconChanged", R.string.TopicIconChanged);
messageTextForReply = AndroidUtilities.replaceCharSequence("%s", LocaleController.getString("TopicIconChangedToInReply", R.string.TopicIconChangedToInReply), topicName);
} else if ((messageOwner.action.flags & 1) != 0) {
CharSequence str = AndroidUtilities.replaceCharSequence("%1$s", LocaleController.getString("TopicRenamedTo", R.string.TopicRenamedTo), name);
messageText = AndroidUtilities.replaceCharSequence("%2$s", str, editAction.title);
messageTextShort = LocaleController.getString("TopicRenamed", R.string.TopicRenamed);
messageTextForReply = AndroidUtilities.replaceCharSequence("%s", LocaleController.getString("TopicRenamedToInReply", R.string.TopicRenamedToInReply), editAction.title);
}
}
} else if (messageOwner.action instanceof TLRPC.TL_messageActionGameScore) {
generateGameMessageText(fromUser);
} else if (messageOwner.action instanceof TLRPC.TL_messageActionPhoneCall) {
@ -3412,6 +3643,8 @@ public class MessageObject {
} else if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaPhoto) {
if (getMedia(messageOwner).ttl_seconds != 0 && !(messageOwner instanceof TLRPC.TL_message_secret)) {
messageText = LocaleController.getString("AttachDestructingPhoto", R.string.AttachDestructingPhoto);
} else if (getGroupId() != 0) {
messageText = LocaleController.getString("Album", R.string.Album);
} else {
messageText = LocaleController.getString("AttachPhoto", R.string.AttachPhoto);
}
@ -3483,6 +3716,13 @@ public class MessageObject {
}
}
public static TLRPC.MessageMedia getMedia(MessageObject messageObject) {
if (messageObject == null || messageObject.messageOwner == null) {
return null;
}
return getMedia(messageObject.messageOwner);
}
public static TLRPC.MessageMedia getMedia(TLRPC.Message messageOwner) {
if (messageOwner.media != null && messageOwner.media.extended_media instanceof TLRPC.TL_messageExtendedMedia) {
return ((TLRPC.TL_messageExtendedMedia) messageOwner.media.extended_media).media;
@ -3508,7 +3748,7 @@ public class MessageObject {
isRoundVideoCached = 0;
if (messageOwner instanceof TLRPC.TL_message || messageOwner instanceof TLRPC.TL_messageForwarded_old2) {
if (isRestrictedMessage) {
type = 0;
type = TYPE_TEXT;
} else if (emojiAnimatedSticker != null || emojiAnimatedStickerId != null) {
if (isSticker()) {
type = TYPE_STICKER;
@ -3518,9 +3758,9 @@ public class MessageObject {
} else if (!isDice() && emojiOnlyCount >= 1 && !hasUnwrappedEmoji) {
type = TYPE_EMOJIS;
} else if (isMediaEmpty()) {
type = 0;
type = TYPE_TEXT;
if (TextUtils.isEmpty(messageText) && eventId == 0) {
messageText = "Empty message";
messageText = LocaleController.getString("EventLogOriginalCaptionEmpty", R.string.EventLogOriginalCaptionEmpty);
}
} else if (hasExtendedMediaPreview()) {
type = TYPE_EXTENDED_MEDIA_PREVIEW;
@ -3551,43 +3791,43 @@ public class MessageObject {
} else if (isVoice()) {
type = TYPE_VOICE;
} else if (isMusic()) {
type = 14;
type = TYPE_MUSIC;
} else if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaContact) {
type = 12;
type = TYPE_CONTACT;
} else if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaPoll) {
type = TYPE_POLL;
checkedVotes = new ArrayList<>();
} else if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaUnsupported) {
type = 0;
type = TYPE_TEXT;
} else if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaDocument) {
TLRPC.Document document = getDocument();
if (document != null && document.mime_type != null) {
if (isGifDocument(document, hasValidGroupId())) {
type = 8;
type = TYPE_GIF;
} else if (isSticker()) {
type = TYPE_STICKER;
} else if (isAnimatedSticker()) {
type = TYPE_ANIMATED_STICKER;
} else {
type = 9;
type = TYPE_FILE;
}
} else {
type = 9;
type = TYPE_FILE;
}
} else if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaGame) {
type = 0;
type = TYPE_TEXT;
} else if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaInvoice) {
type = 0;
type = TYPE_TEXT;
}
} else if (messageOwner instanceof TLRPC.TL_messageService) {
if (messageOwner.action instanceof TLRPC.TL_messageActionLoginUnknownLocation) {
type = 0;
type = TYPE_TEXT;
} else if (messageOwner.action instanceof TLRPC.TL_messageActionGiftPremium) {
contentType = 1;
type = TYPE_GIFT_PREMIUM;
} else if (messageOwner.action instanceof TLRPC.TL_messageActionChatEditPhoto || messageOwner.action instanceof TLRPC.TL_messageActionUserUpdatedPhoto) {
contentType = 1;
type = 11;
type = TYPE_ACTION_PHOTO;
} else if (messageOwner.action instanceof TLRPC.TL_messageEncryptedAction) {
if (messageOwner.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionScreenshotMessages || messageOwner.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionSetMessageTTL) {
contentType = 1;
@ -3600,7 +3840,7 @@ public class MessageObject {
contentType = -1;
type = -1;
} else if (messageOwner.action instanceof TLRPC.TL_messageActionPhoneCall) {
type = 16;
type = TYPE_PHONE_CALL;
} else {
contentType = 1;
type = 10;
@ -3613,7 +3853,7 @@ public class MessageObject {
}
public boolean checkLayout() {
if (type != 0 && type != TYPE_EMOJIS || messageOwner.peer_id == null || messageText == null || messageText.length() == 0) {
if (type != TYPE_TEXT && type != TYPE_EMOJIS || messageOwner.peer_id == null || messageText == null || messageText.length() == 0) {
return false;
}
if (layoutCreated) {
@ -3990,30 +4230,33 @@ public class MessageObject {
public static CharSequence replaceWithLink(CharSequence source, String param, TLObject object) {
int start = TextUtils.indexOf(source, param);
if (start >= 0) {
String name;
CharSequence name;
String id;
TLObject spanObject = null;
if (object instanceof TLRPC.User) {
name = UserObject.getUserName((TLRPC.User) object);
name = UserObject.getUserName((TLRPC.User) object).replace('\n', ' ');
id = "" + ((TLRPC.User) object).id;
} else if (object instanceof TLRPC.Chat) {
name = ((TLRPC.Chat) object).title;
name = ((TLRPC.Chat) object).title.replace('\n', ' ');
id = "" + -((TLRPC.Chat) object).id;
} else if (object instanceof TLRPC.TL_game) {
TLRPC.TL_game game = (TLRPC.TL_game) object;
name = game.title;
name = game.title.replace('\n', ' ');
id = "game";
} else if (object instanceof TLRPC.TL_chatInviteExported) {
TLRPC.TL_chatInviteExported invite = (TLRPC.TL_chatInviteExported) object;
name = invite.link;
name = invite.link.replace('\n', ' ');
id = "invite";
spanObject = invite;
} else if (object instanceof TLRPC.ForumTopic) {
name = ForumUtilities.getTopicSpannedName((TLRPC.ForumTopic) object, null);
id = "topic";
spanObject = object;
} else {
name = "";
id = "0";
}
name = name.replace('\n', ' ');
SpannableStringBuilder builder = new SpannableStringBuilder(TextUtils.replace(source, new String[]{param}, new String[]{name}));
SpannableStringBuilder builder = new SpannableStringBuilder(TextUtils.replace(source, new String[]{param}, new CharSequence[]{name}));
URLSpanNoUnderlineBold span = new URLSpanNoUnderlineBold("" + id);
span.setObject(spanObject);
builder.setSpan(span, start, start + name.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
@ -4218,7 +4461,14 @@ public class MessageObject {
}
public boolean isVoiceTranscriptionOpen() {
return isVoice() && messageOwner != null && messageOwner.voiceTranscriptionOpen && messageOwner.voiceTranscription != null && (messageOwner.voiceTranscriptionFinal || TranscribeButton.isTranscribing(this)) && UserConfig.getInstance(currentAccount).isPremium();
return (
UserConfig.getInstance(currentAccount).isPremium() &&
messageOwner != null &&
(isVoice() || isRoundVideo() && TranscribeButton.isVideoTranscriptionOpen(this)) &&
messageOwner.voiceTranscriptionOpen &&
messageOwner.voiceTranscription != null &&
(messageOwner.voiceTranscriptionFinal || TranscribeButton.isTranscribing(this))
);
}
public void generateCaption() {
@ -4821,15 +5071,18 @@ public class MessageObject {
return true;
}
if (!isOut()) {
if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaGame || getMedia(messageOwner) instanceof TLRPC.TL_messageMediaInvoice && !hasExtendedMedia()) {
if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaGame || getMedia(messageOwner) instanceof TLRPC.TL_messageMediaInvoice && !hasExtendedMedia() || getMedia(messageOwner) instanceof TLRPC.TL_messageMediaWebPage) {
return true;
}
TLRPC.Chat chat = messageOwner.peer_id != null && messageOwner.peer_id.channel_id != 0 ? getChat(null, null, messageOwner.peer_id.channel_id) : null;
if (ChatObject.isChannel(chat) && chat.megagroup) {
return chat.username != null && chat.username.length() > 0 && !(getMedia(messageOwner) instanceof TLRPC.TL_messageMediaContact) && !(getMedia(messageOwner) instanceof TLRPC.TL_messageMediaGeo);
return ChatObject.isPublic(chat) && !(getMedia(messageOwner) instanceof TLRPC.TL_messageMediaContact) && !(getMedia(messageOwner) instanceof TLRPC.TL_messageMediaGeo);
}
}
} else if (messageOwner.from_id instanceof TLRPC.TL_peerChannel || messageOwner.post) {
if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaWebPage && !isOutOwner()) {
return true;
}
if (isSupergroup()) {
return false;
}
@ -4883,7 +5136,7 @@ public class MessageObject {
}
public void generateLayout(TLRPC.User fromUser) {
if (type != 0 && type != TYPE_EMOJIS || messageOwner.peer_id == null || TextUtils.isEmpty(messageText)) {
if (type != TYPE_TEXT && type != TYPE_EMOJIS || messageOwner.peer_id == null || TextUtils.isEmpty(messageText)) {
return;
}
@ -5205,7 +5458,7 @@ public class MessageObject {
if (customAvatarDrawable != null) {
return true;
}
if (isSponsored() && isFromChat()) {
if (isSponsored() && (isFromChat() || sponsoredShowPeerPhoto)) {
return true;
}
return !isSponsored() && (isFromUser() || isFromGroup() || eventId != 0 || messageOwner.fwd_from != null && messageOwner.fwd_from.saved_from_peer != null);
@ -5249,6 +5502,25 @@ public class MessageObject {
}
}
public static boolean peersEqual(TLRPC.Peer a, TLRPC.Peer b) {
if (a == null && b == null) {
return true;
}
if (a == null || b == null) {
return false;
}
if (a instanceof TLRPC.TL_peerChat && b instanceof TLRPC.TL_peerChat) {
return a.chat_id == b.chat_id;
}
if (a instanceof TLRPC.TL_peerChannel && b instanceof TLRPC.TL_peerChannel) {
return a.channel_id == b.channel_id;
}
if (a instanceof TLRPC.TL_peerUser && b instanceof TLRPC.TL_peerUser) {
return a.user_id == b.user_id;
}
return false;
}
public long getFromChatId() {
return getFromChatId(messageOwner);
}
@ -5552,7 +5824,7 @@ public class MessageObject {
for (int a = 0; a < document.attributes.size(); a++) {
TLRPC.DocumentAttribute attribute = document.attributes.get(a);
if (attribute instanceof TLRPC.TL_documentAttributeSticker ||
attribute instanceof TLRPC.TL_documentAttributeCustomEmoji) {
attribute instanceof TLRPC.TL_documentAttributeCustomEmoji) {
return "video/webm".equals(document.mime_type);
}
}
@ -5937,7 +6209,7 @@ public class MessageObject {
}
public int getApproximateHeight() {
if (type == 0) {
if (type == TYPE_TEXT) {
int height = textHeight + (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaWebPage && getMedia(messageOwner).webpage instanceof TLRPC.TL_webPage ? AndroidUtilities.dp(100) : 0);
if (isReply()) {
height += AndroidUtilities.dp(42);
@ -5947,17 +6219,17 @@ public class MessageObject {
return AndroidUtilities.getPhotoSize();
} else if (type == TYPE_VOICE) {
return AndroidUtilities.dp(72);
} else if (type == 12) {
} else if (type == TYPE_CONTACT) {
return AndroidUtilities.dp(71);
} else if (type == 9) {
} else if (type == TYPE_FILE) {
return AndroidUtilities.dp(100);
} else if (type == 4) {
} else if (type == TYPE_GEO) {
return AndroidUtilities.dp(114);
} else if (type == 14) {
} else if (type == TYPE_MUSIC) {
return AndroidUtilities.dp(82);
} else if (type == 10) {
return AndroidUtilities.dp(30);
} else if (type == 11 || type == TYPE_GIFT_PREMIUM) {
} else if (type == TYPE_ACTION_PHOTO || type == TYPE_GIFT_PREMIUM) {
return AndroidUtilities.dp(50);
} else if (type == TYPE_ROUND_VIDEO) {
return AndroidUtilities.roundMessageSize;
@ -6460,7 +6732,7 @@ public class MessageObject {
}
public boolean canForwardMessage() {
return !(messageOwner instanceof TLRPC.TL_message_secret) && !needDrawBluredPreview() && !isLiveLocation() && type != 16 && !isSponsored() && !messageOwner.noforwards;
return !(messageOwner instanceof TLRPC.TL_message_secret) && !needDrawBluredPreview() && !isLiveLocation() && type != MessageObject.TYPE_PHONE_CALL && !isSponsored() && !messageOwner.noforwards;
}
public boolean canEditMedia() {
@ -6754,14 +7026,14 @@ public class MessageObject {
}
}
}
if (!mediaExists && type == 8 || type == TYPE_VIDEO || type == 9 || type == TYPE_VOICE || type == 14 || type == TYPE_ROUND_VIDEO) {
if (!mediaExists && type == TYPE_GIF || type == TYPE_VIDEO || type == TYPE_FILE || type == TYPE_VOICE || type == TYPE_MUSIC || type == TYPE_ROUND_VIDEO) {
if (messageOwner.attachPath != null && messageOwner.attachPath.length() > 0) {
File f = new File(messageOwner.attachPath);
attachPathExists = f.exists();
}
if (!attachPathExists) {
File file = FileLoader.getInstance(currentAccount).getPathToMessage(messageOwner, useFileDatabaseQueue);
if (type == 3 && needDrawBluredPreview()) {
if (type == TYPE_VIDEO && needDrawBluredPreview()) {
mediaExists = new File(file.getAbsolutePath() + ".enc").exists();
}
if (!mediaExists) {
@ -6777,13 +7049,13 @@ public class MessageObject {
} else {
mediaExists = FileLoader.getInstance(currentAccount).getPathToAttach(document, null, false, useFileDatabaseQueue).exists();
}
} else if (type == 0) {
} else if (type == MessageObject.TYPE_TEXT) {
TLRPC.PhotoSize currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(photoThumbs, AndroidUtilities.getPhotoSize());
if (currentPhotoObject == null) {
return;
}
mediaExists = FileLoader.getInstance(currentAccount).getPathToAttach(currentPhotoObject, null, true, useFileDatabaseQueue).exists();
} else if (type == 11) {
} else if (type == MessageObject.TYPE_ACTION_PHOTO) {
TLRPC.Photo photo = messageOwner.action.photo;
if (photo == null || photo.video_sizes.isEmpty()) {
return;
@ -7004,10 +7276,12 @@ public class MessageObject {
int maxReactionsCount = MessagesController.getInstance(currentAccount).getMaxUserReactionsCount();
if (!choosenReactions.isEmpty() && (choosenReactions.contains(newReaction) || fromDoubleTap)) {
newReaction.chosen = false;
newReaction.count--;
if (newReaction.count <= 0) {
messageOwner.reactions.results.remove(newReaction);
if (newReaction != null) {
newReaction.chosen = false;
newReaction.count--;
if (newReaction.count <= 0) {
messageOwner.reactions.results.remove(newReaction);
}
}
if (messageOwner.reactions.can_see_list) {
for (int i = 0; i < messageOwner.reactions.recent_reactions.size(); i++) {
@ -7093,4 +7367,29 @@ public class MessageObject {
}
return false;
}
public byte[] getWaveform() {
if (getDocument() == null) {
return null;
}
for (int a = 0; a < getDocument().attributes.size(); a++) {
TLRPC.DocumentAttribute attribute = getDocument().attributes.get(a);
if (attribute instanceof TLRPC.TL_documentAttributeAudio) {
if (attribute.waveform == null || attribute.waveform.length == 0) {
MediaController.getInstance().generateWaveform(this);
}
return attribute.waveform;
}
}
if (isRoundVideo()) {
if (randomWaveform == null) {
randomWaveform = new byte[120];
for (int i = 0; i < randomWaveform.length; ++i) {
randomWaveform[i] = (byte) (255 * Math.random());
}
}
return randomWaveform;
}
return null;
}
}

View File

@ -9,7 +9,6 @@
package org.telegram.messenger;
import android.os.SystemClock;
import android.util.Log;
import android.util.SparseArray;
import androidx.annotation.UiThread;
@ -262,6 +261,8 @@ public class NotificationCenter {
public static final int userEmojiStatusUpdated = totalEvents++;
public static final int requestPermissions = totalEvents++;
public static final int permissionsGranted = totalEvents++;
public static int topicsDidLoaded = totalEvents++;
public static int chatSwithcedToForum = totalEvents++;
private SparseArray<ArrayList<NotificationCenterDelegate>> observers = new SparseArray<>();
private SparseArray<ArrayList<NotificationCenterDelegate>> removeAfterBroadcast = new SparseArray<>();

View File

@ -69,6 +69,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.function.Consumer;
public class NotificationsController extends BaseController {
@ -91,6 +92,7 @@ public class NotificationsController extends BaseController {
public ArrayList<MessageObject> popupReplyMessages = new ArrayList<>();
private HashSet<Long> openedInBubbleDialogs = new HashSet<>();
private long openedDialogId = 0;
private int openedTopicId = 0;
private int lastButtonId = 5000;
private int total_unread_count = 0;
private int personalCount = 0;
@ -132,6 +134,8 @@ public class NotificationsController extends BaseController {
public static final int SETTING_SOUND_ON = 0;
public static final int SETTING_SOUND_OFF = 1;
NotificationsSettingsFacade dialogsNotificationsFacade;
static {
if (Build.VERSION.SDK_INT >= 26 && ApplicationLoader.applicationContext != null) {
notificationManager = NotificationManagerCompat.from(ApplicationLoader.applicationContext);
@ -210,6 +214,8 @@ public class NotificationsController extends BaseController {
FileLog.e(e);
}
};
dialogsNotificationsFacade = new NotificationsSettingsFacade(currentAccount);
}
public static void checkOtherNotificationsChannel() {
@ -247,37 +253,49 @@ public class NotificationsController extends BaseController {
}
}
public void muteUntil(long did, int selectedTimeInSeconds) {
public static String getSharedPrefKey(long dialog_id, int topicId) {
String str = Long.toString(dialog_id);
if (topicId != 0) {
str += "_" + topicId;
}
return str;
}
public void muteUntil(long did, int topicId, int selectedTimeInSeconds) {
if (did != 0) {
SharedPreferences preferences = MessagesController.getNotificationsSettings(currentAccount);
SharedPreferences.Editor editor = preferences.edit();
long flags;
boolean override = topicId != 0;
boolean defaultEnabled = NotificationsController.getInstance(currentAccount).isGlobalNotificationsEnabled(did);
String sharedPrefKey = NotificationsController.getSharedPrefKey(did, topicId);
if (selectedTimeInSeconds == Integer.MAX_VALUE) {
if (!defaultEnabled) {
editor.remove("notify2_" + did);
if (!defaultEnabled && !override) {
editor.remove("notify2_" + sharedPrefKey);
flags = 0;
} else {
editor.putInt("notify2_" + did, 2);
editor.putInt("notify2_" + sharedPrefKey, 2);
flags = 1;
}
} else {
editor.putInt("notify2_" + did, 3);
editor.putInt("notifyuntil_" + did, getConnectionsManager().getCurrentTime() + selectedTimeInSeconds);
editor.putInt("notify2_" + sharedPrefKey, 3);
editor.putInt("notifyuntil_" + sharedPrefKey, getConnectionsManager().getCurrentTime() + selectedTimeInSeconds);
flags = ((long) selectedTimeInSeconds << 32) | 1;
}
NotificationsController.getInstance(currentAccount).removeNotificationsForDialog(did);
MessagesStorage.getInstance(currentAccount).setDialogFlags(did, flags);
editor.commit();
TLRPC.Dialog dialog = MessagesController.getInstance(currentAccount).dialogs_dict.get(did);
if (dialog != null) {
dialog.notify_settings = new TLRPC.TL_peerNotifySettings();
if (selectedTimeInSeconds != Integer.MAX_VALUE || defaultEnabled) {
dialog.notify_settings.mute_until = selectedTimeInSeconds;
editor.apply();
if (topicId == 0) {
NotificationsController.getInstance(currentAccount).removeNotificationsForDialog(did);
MessagesStorage.getInstance(currentAccount).setDialogFlags(did, flags);
TLRPC.Dialog dialog = MessagesController.getInstance(currentAccount).dialogs_dict.get(did);
if (dialog != null) {
dialog.notify_settings = new TLRPC.TL_peerNotifySettings();
if (selectedTimeInSeconds != Integer.MAX_VALUE || defaultEnabled) {
dialog.notify_settings.mute_until = selectedTimeInSeconds;
}
}
}
NotificationsController.getInstance(currentAccount).updateServerNotificationsSettings(did);
NotificationsController.getInstance(currentAccount).updateServerNotificationsSettings(did, topicId);
}
}
@ -287,6 +305,7 @@ public class NotificationsController extends BaseController {
channelGroupsCreated = false;
notificationsQueue.postRunnable(() -> {
openedDialogId = 0;
openedTopicId = 0;
total_unread_count = 0;
personalCount = 0;
pushMessages.clear();
@ -348,8 +367,11 @@ public class NotificationsController extends BaseController {
inChatSoundEnabled = value;
}
public void setOpenedDialogId(long dialog_id) {
notificationsQueue.postRunnable(() -> openedDialogId = dialog_id);
public void setOpenedDialogId(long dialog_id, int topicId) {
notificationsQueue.postRunnable(() -> {
openedDialogId = dialog_id;
openedTopicId = topicId;
});
}
public void setOpenedInBubble(long dialogId, boolean opened) {
@ -765,7 +787,8 @@ public class NotificationsController extends BaseController {
MessageObject messageObject = messageObjects.get(a);
if (messageObject.messageOwner != null && (messageObject.isImportedForward() ||
messageObject.messageOwner.action instanceof TLRPC.TL_messageActionSetMessagesTTL ||
messageObject.messageOwner.silent && (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionContactSignUp || messageObject.messageOwner.action instanceof TLRPC.TL_messageActionUserJoined))) {
messageObject.messageOwner.silent && (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionContactSignUp || messageObject.messageOwner.action instanceof TLRPC.TL_messageActionUserJoined)) ||
MessageObject.isTopicActionMessage(messageObject)) {
continue;
}
int mid = messageObject.getId();
@ -820,6 +843,7 @@ public class NotificationsController extends BaseController {
}
long originalDialogId = dialogId;
int topicId = MessageObject.getTopicId(messageObject.messageOwner);
if (dialogId == openedDialogId && ApplicationLoader.isScreenOn) {
if (!isFcm) {
playInChatSound();
@ -840,10 +864,10 @@ public class NotificationsController extends BaseController {
boolean isChat = DialogObject.isChatDialog(dialogId);
int index = settingsCache.indexOfKey(dialogId);
boolean value;
if (index >= 0) {
if (index >= 0 && topicId == 0) {
value = settingsCache.valueAt(index);
} else {
int notifyOverride = getNotifyOverride(preferences, dialogId);
int notifyOverride = getNotifyOverride(preferences, dialogId, topicId);
if (notifyOverride == -1) {
value = isGlobalNotificationsEnabled(dialogId, isChannel);
/*if (BuildVars.DEBUG_PRIVATE_VERSION && BuildVars.LOGS_ENABLED) {
@ -882,7 +906,7 @@ public class NotificationsController extends BaseController {
if (messageObject.isReactionPush) {
SparseBooleanArray sparseBooleanArray = new SparseBooleanArray();
sparseBooleanArray.put(mid, true);
getMessagesController().checkUnreadReactions(dialogId, sparseBooleanArray);
getMessagesController().checkUnreadReactions(dialogId, MessageObject.getTopicId(messageObject.messageOwner), sparseBooleanArray);
}
}
@ -914,6 +938,7 @@ public class NotificationsController extends BaseController {
} else if (added) {
MessageObject messageObject = messageObjects.get(0);
long dialog_id = messageObject.getDialogId();
int topicId = MessageObject.getTopicId(messageObject.messageOwner);
Boolean isChannel;
if (messageObject.isFcmMessage()) {
isChannel = messageObject.localChannel;
@ -922,7 +947,7 @@ public class NotificationsController extends BaseController {
}
int old_unread_count = total_unread_count;
int notifyOverride = getNotifyOverride(preferences, dialog_id);
int notifyOverride = getNotifyOverride(preferences, dialog_id, topicId);
boolean canAddValue;
if (notifyOverride == -1) {
canAddValue = isGlobalNotificationsEnabled(dialog_id, isChannel);
@ -982,20 +1007,28 @@ public class NotificationsController extends BaseController {
long dialogId = dialogsToUpdate.keyAt(b);
Integer currentCount = pushDialogs.get(dialogId);
int newCount = dialogsToUpdate.get(dialogId);
boolean forum = false;
if (DialogObject.isChatDialog(dialogId)) {
TLRPC.Chat chat = getMessagesController().getChat(-dialogId);
if (chat == null || chat.min || ChatObject.isNotInChat(chat)) {
newCount = 0;
}
if (chat != null) {
forum = chat.forum;
}
}
int notifyOverride = getNotifyOverride(preferences, dialogId);
boolean canAddValue;
if (notifyOverride == -1) {
canAddValue = isGlobalNotificationsEnabled(dialogId);
if (!forum) {
int notifyOverride = getNotifyOverride(preferences, dialogId, 0);
if (notifyOverride == -1) {
canAddValue = isGlobalNotificationsEnabled(dialogId);
} else {
canAddValue = notifyOverride != 2;
}
} else {
canAddValue = notifyOverride != 2;
canAddValue = true;
}
if (notifyCheck && !canAddValue) {
@ -1123,15 +1156,16 @@ public class NotificationsController extends BaseController {
}
long dialog_id = messageObject.getDialogId();
long original_dialog_id = dialog_id;
int topicId = MessageObject.getTopicId(messageObject.messageOwner);
if (messageObject.messageOwner.mentioned) {
dialog_id = messageObject.getFromChatId();
}
int index = settingsCache.indexOfKey(dialog_id);
boolean value;
if (index >= 0) {
if (index >= 0 && topicId == 0) {
value = settingsCache.valueAt(index);
} else {
int notifyOverride = getNotifyOverride(preferences, dialog_id);
int notifyOverride = getNotifyOverride(preferences, dialog_id, topicId);
if (notifyOverride == -1) {
value = isGlobalNotificationsEnabled(dialog_id);
} else {
@ -1161,7 +1195,7 @@ public class NotificationsController extends BaseController {
if (index >= 0) {
value = settingsCache.valueAt(index);
} else {
int notifyOverride = getNotifyOverride(preferences, dialog_id);
int notifyOverride = getNotifyOverride(preferences, dialog_id, 0);
if (notifyOverride == -1) {
value = isGlobalNotificationsEnabled(dialog_id);
} else {
@ -1190,16 +1224,17 @@ public class NotificationsController extends BaseController {
}
long dialogId = messageObject.getDialogId();
long originalDialogId = dialogId;
int topicId = MessageObject.getTopicId(messageObject.messageOwner);
long randomId = messageObject.messageOwner.random_id;
if (messageObject.messageOwner.mentioned) {
dialogId = messageObject.getFromChatId();
}
int index = settingsCache.indexOfKey(dialogId);
boolean value;
if (index >= 0) {
if (index >= 0 && topicId == 0) {
value = settingsCache.valueAt(index);
} else {
int notifyOverride = getNotifyOverride(preferences, dialogId);
int notifyOverride = getNotifyOverride(preferences, dialogId, topicId);
if (notifyOverride == -1) {
value = isGlobalNotificationsEnabled(dialogId);
} else {
@ -2591,10 +2626,10 @@ public class NotificationsController extends BaseController {
&& (messageObject.messageOwner.action == null || messageObject.messageOwner.action instanceof TLRPC.TL_messageActionEmpty);
}
private int getNotifyOverride(SharedPreferences preferences, long dialog_id) {
int notifyOverride = preferences.getInt("notify2_" + dialog_id, -1);
private int getNotifyOverride(SharedPreferences preferences, long dialog_id, int topicId) {
int notifyOverride = dialogsNotificationsFacade.getProperty(NotificationsSettingsFacade.PROPERTY_NOTIFY, dialog_id, topicId, -1);
if (notifyOverride == 3) {
int muteUntil = preferences.getInt("notifyuntil_" + dialog_id, 0);
int muteUntil = dialogsNotificationsFacade.getProperty(NotificationsSettingsFacade.PROPERTY_NOTIFY_UNTIL, dialog_id, topicId, 0);
if (muteUntil >= getConnectionsManager().getCurrentTime()) {
notifyOverride = 2;
}
@ -2654,7 +2689,7 @@ public class NotificationsController extends BaseController {
try {
SharedPreferences preferences = getAccountInstance().getNotificationsSettings();
int notifyOverride = getNotifyOverride(preferences, openedDialogId);
int notifyOverride = getNotifyOverride(preferences, openedDialogId, openedTopicId);
if (notifyOverride == 2) {
return;
}
@ -2733,11 +2768,11 @@ public class NotificationsController extends BaseController {
return true;
}
public void deleteNotificationChannel(long dialogId) {
deleteNotificationChannel(dialogId, -1);
public void deleteNotificationChannel(long dialogId, int topicId) {
deleteNotificationChannel(dialogId, topicId, -1);
}
private void deleteNotificationChannelInternal(long dialogId, int what) {
private void deleteNotificationChannelInternal(long dialogId, int topicId, int what) {
if (Build.VERSION.SDK_INT < 26) {
return;
}
@ -2746,6 +2781,9 @@ public class NotificationsController extends BaseController {
SharedPreferences.Editor editor = preferences.edit();
if (what == 0 || what == -1) {
String key = "org.telegram.key" + dialogId;
if (topicId != 0) {
key += ".topic" + topicId;
}
String channelId = preferences.getString(key, null);
if (channelId != null) {
editor.remove(key).remove(key + "_s");
@ -2780,11 +2818,11 @@ public class NotificationsController extends BaseController {
}
}
public void deleteNotificationChannel(long dialogId, int what) {
public void deleteNotificationChannel(long dialogId, int topicId, int what) {
if (Build.VERSION.SDK_INT < 26) {
return;
}
notificationsQueue.postRunnable(() -> deleteNotificationChannelInternal(dialogId, what));
notificationsQueue.postRunnable(() -> deleteNotificationChannelInternal(dialogId, topicId, what));
}
public void deleteNotificationChannelGlobal(int type) {
@ -3079,7 +3117,7 @@ public class NotificationsController extends BaseController {
}
@TargetApi(26)
private String validateChannelId(long dialogId, String name, long[] vibrationPattern, int ledColor, Uri sound, int importance, boolean isDefault, boolean isInApp, boolean isSilent, int type) {
private String validateChannelId(long dialogId, int topicId, String name, long[] vibrationPattern, int ledColor, Uri sound, int importance, boolean isDefault, boolean isInApp, boolean isSilent, int type) {
ensureGroupsCreated();
SharedPreferences preferences = getAccountInstance().getNotificationsSettings();
@ -3126,7 +3164,8 @@ public class NotificationsController extends BaseController {
if (isInApp) {
name = LocaleController.formatString("NotificationsChatInApp", R.string.NotificationsChatInApp, name);
}
key = (isInApp ? "org.telegram.keyia" : "org.telegram.key") + dialogId;
//TODO notifications
key = (isInApp ? "org.telegram.keyia" : "org.telegram.key") + dialogId + "_" + topicId;
}
key += "_" + soundHash;
String channelId = preferences.getString(key, null);
@ -3178,8 +3217,8 @@ public class NotificationsController extends BaseController {
updateServerNotificationsSettings(type);
}
} else {
editor.putInt("notify2_" + dialogId, 2);
updateServerNotificationsSettings(dialogId, true);
editor.putInt("notify2_" + NotificationsController.getSharedPrefKey(dialogId, 0), 2);
updateServerNotificationsSettings(dialogId, 0,true);
}
edited = true;
} else if (channelImportance != importance) {
@ -3352,6 +3391,8 @@ public class NotificationsController extends BaseController {
}
long dialog_id = lastMessageObject.getDialogId();
int topicId = MessageObject.getTopicId(lastMessageObject.messageOwner);
boolean isChannel = false;
long override_dialog_id = dialog_id;
if (lastMessageObject.messageOwner.mentioned) {
@ -3383,7 +3424,7 @@ public class NotificationsController extends BaseController {
int ledColor = 0xff0000ff;
int importance = 0;
int notifyOverride = getNotifyOverride(preferences, override_dialog_id);
int notifyOverride = getNotifyOverride(preferences, override_dialog_id, topicId);
boolean value;
if (notifyOverride == -1) {
value = isGlobalNotificationsEnabled(dialog_id, isChannel);
@ -3531,7 +3572,7 @@ public class NotificationsController extends BaseController {
}
}
if (!notifyDisabled && !preferences.getBoolean("sound_enabled_" + dialog_id, true)) {
if (!notifyDisabled && !preferences.getBoolean("sound_enabled_" + getSharedPrefKey(dialog_id, topicId), true)) {
notifyDisabled = true;
}
@ -3546,18 +3587,21 @@ public class NotificationsController extends BaseController {
int customVibrate;
int customImportance;
Integer customLedColor;
if (preferences.getBoolean("custom_" + dialog_id, false)) {
customVibrate = preferences.getInt("vibrate_" + dialog_id, 0);
customImportance = preferences.getInt("priority_" + dialog_id, 3);
long soundDocumentId = preferences.getLong("sound_document_id_" + dialog_id, 0);
String key = getSharedPrefKey(dialog_id, topicId);
if (dialogsNotificationsFacade.getProperty("custom_", dialog_id, topicId, false)) {
customVibrate = dialogsNotificationsFacade.getProperty("vibrate_", dialog_id, topicId, 0);
customImportance = dialogsNotificationsFacade.getProperty("priority_", dialog_id, topicId, 3);
long soundDocumentId = dialogsNotificationsFacade.getProperty("sound_document_id_" , dialog_id, topicId, 0L);
if (soundDocumentId != 0) {
customIsInternalSound = true;
customSoundPath = getMediaDataController().ringtoneDataStore.getSoundPath(soundDocumentId);
} else {
customSoundPath = preferences.getString("sound_path_" + dialog_id, null);
customSoundPath = dialogsNotificationsFacade.getPropertyString("sound_path_" , dialog_id, topicId, null);
}
if (preferences.contains("color_" + dialog_id)) {
customLedColor = preferences.getInt("color_" + dialog_id, 0);
int color = dialogsNotificationsFacade.getProperty("color_", dialog_id, topicId, 0);
if (color != 0) {
customLedColor = color;
} else {
customLedColor = null;
}
@ -3858,7 +3902,7 @@ public class NotificationsController extends BaseController {
mBuilder.addAction(R.drawable.ic_ab_reply, LocaleController.getString("Reply", R.string.Reply), PendingIntent.getBroadcast(ApplicationLoader.applicationContext, 2, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT));
}
}
showExtraNotifications(mBuilder, detailText, dialog_id, chatName, vibrationPattern, ledColor, sound, configImportance, isDefault, isInApp, notifyDisabled, chatType);
showExtraNotifications(mBuilder, detailText, dialog_id, topicId, chatName, vibrationPattern, ledColor, sound, configImportance, isDefault, isInApp, notifyDisabled, chatType);
scheduleNotificationRepeat();
} catch (Exception e) {
FileLog.e(e);
@ -3878,7 +3922,7 @@ public class NotificationsController extends BaseController {
}
}
private void resetNotificationSound(NotificationCompat.Builder notificationBuilder, long dialogId, String chatName, long[] vibrationPattern, int ledColor, Uri sound, int importance, boolean isDefault, boolean isInApp, boolean isSilent, int chatType) {
private void resetNotificationSound(NotificationCompat.Builder notificationBuilder, long dialogId, int topicId, String chatName, long[] vibrationPattern, int ledColor, Uri sound, int importance, boolean isDefault, boolean isInApp, boolean isSilent, int chatType) {
Uri defaultSound = Settings.System.DEFAULT_RINGTONE_URI;
if (defaultSound != null && sound != null && !TextUtils.equals(defaultSound.toString(), sound.toString())) {
SharedPreferences preferences = getAccountInstance().getNotificationsSettings();
@ -3903,21 +3947,21 @@ public class NotificationsController extends BaseController {
}
getNotificationsController().deleteNotificationChannelGlobalInternal(chatType, -1);
} else {
editor.putString("sound_" + dialogId, ringtoneName);
editor.putString("sound_path_" + dialogId, newSound);
deleteNotificationChannelInternal(dialogId, -1);
editor.putString("sound_" + NotificationsController.getSharedPrefKey(dialogId, topicId), ringtoneName);
editor.putString("sound_path_" + NotificationsController.getSharedPrefKey(dialogId, topicId), newSound);
deleteNotificationChannelInternal(dialogId, topicId, -1);
}
editor.commit();
sound = Settings.System.DEFAULT_RINGTONE_URI;
notificationBuilder.setChannelId(validateChannelId(dialogId, chatName, vibrationPattern, ledColor, sound, importance, isDefault, isInApp, isSilent, chatType));
notificationBuilder.setChannelId(validateChannelId(dialogId, topicId, chatName, vibrationPattern, ledColor, sound, importance, isDefault, isInApp, isSilent, chatType));
notificationManager.notify(notificationId, notificationBuilder.build());
}
}
@SuppressLint("InlinedApi")
private void showExtraNotifications(NotificationCompat.Builder notificationBuilder, String summary, long lastDialogId, String chatName, long[] vibrationPattern, int ledColor, Uri sound, int importance, boolean isDefault, boolean isInApp, boolean isSilent, int chatType) {
private void showExtraNotifications(NotificationCompat.Builder notificationBuilder, String summary, long lastDialogId, int lastTopicId, String chatName, long[] vibrationPattern, int ledColor, Uri sound, int importance, boolean isDefault, boolean isInApp, boolean isSilent, int chatType) {
if (Build.VERSION.SDK_INT >= 26) {
notificationBuilder.setChannelId(validateChannelId(lastDialogId, chatName, vibrationPattern, ledColor, sound, importance, isDefault, isInApp, isSilent, chatType));
notificationBuilder.setChannelId(validateChannelId(lastDialogId, lastTopicId, chatName, vibrationPattern, ledColor, sound, importance, isDefault, isInApp, isSilent, chatType));
}
Notification mainNotification = notificationBuilder.build();
if (Build.VERSION.SDK_INT < 18) {
@ -3930,11 +3974,12 @@ public class NotificationsController extends BaseController {
SharedPreferences preferences = getAccountInstance().getNotificationsSettings();
ArrayList<Long> sortedDialogs = new ArrayList<>();
ArrayList<DialogKey> sortedDialogs = new ArrayList<>();
LongSparseArray<ArrayList<MessageObject>> messagesByDialogs = new LongSparseArray<>();
for (int a = 0; a < pushMessages.size(); a++) {
MessageObject messageObject = pushMessages.get(a);
long dialog_id = messageObject.getDialogId();
int topicId = MessageObject.getTopicId(messageObject.messageOwner);
int dismissDate = preferences.getInt("dismissDate" + dialog_id, 0);
if (messageObject.messageOwner.date <= dismissDate) {
continue;
@ -3944,7 +3989,7 @@ public class NotificationsController extends BaseController {
if (arrayList == null) {
arrayList = new ArrayList<>();
messagesByDialogs.put(dialog_id, arrayList);
sortedDialogs.add(dialog_id);
sortedDialogs.add(new DialogKey(dialog_id, topicId));
}
arrayList.add(messageObject);
}
@ -3958,18 +4003,20 @@ public class NotificationsController extends BaseController {
class NotificationHolder {
int id;
long dialogId;
int topicId;
String name;
TLRPC.User user;
TLRPC.Chat chat;
NotificationCompat.Builder notification;
NotificationHolder(int i, long li, String n, TLRPC.User u, TLRPC.Chat c, NotificationCompat.Builder builder) {
NotificationHolder(int i, long li, int topicId, String n, TLRPC.User u, TLRPC.Chat c, NotificationCompat.Builder builder) {
id = i;
name = n;
user = u;
chat = c;
notification = builder;
dialogId = li;
this.topicId = topicId;
}
void call() {
@ -3980,7 +4027,7 @@ public class NotificationsController extends BaseController {
notificationManager.notify(id, notification.build());
} catch (SecurityException e) {
FileLog.e(e);
resetNotificationSound(notification, dialogId, chatName, vibrationPattern, ledColor, sound, importance, isDefault, isInApp, isSilent, chatType);
resetNotificationSound(notification, dialogId, lastTopicId, chatName, vibrationPattern, ledColor, sound, importance, isDefault, isInApp, isSilent, chatType);
}
}
}
@ -4001,7 +4048,9 @@ public class NotificationsController extends BaseController {
if (holders.size() >= maxCount) {
break;
}
long dialogId = sortedDialogs.get(b);
DialogKey dialogKey = sortedDialogs.get(b);
long dialogId = dialogKey.dialogId;
int topicId = dialogKey.topicId;
ArrayList<MessageObject> messageObjects = messagesByDialogs.get(dialogId);
int maxId = messageObjects.get(0).getId();
@ -4017,7 +4066,6 @@ public class NotificationsController extends BaseController {
for (int i = 0; i < messageObjects.size(); i++) {
if (maxDate < messageObjects.get(i).messageOwner.date) {
maxDate = messageObjects.get(i).messageOwner.date;
}
}
TLRPC.Chat chat = null;
@ -4074,6 +4122,14 @@ public class NotificationsController extends BaseController {
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;
}
if (topicId != 0) {
TLRPC.TL_forumTopic topic = getMessagesController().getTopicsController().findTopic(chat.id, topicId);
if (topic != null) {
name = topic.title + " in " + name;
}
}
}
}
} else {
@ -4144,6 +4200,7 @@ public class NotificationsController extends BaseController {
Intent replyIntent = new Intent(ApplicationLoader.applicationContext, WearReplyReceiver.class);
replyIntent.putExtra("dialog_id", dialogId);
replyIntent.putExtra("max_id", maxId);
replyIntent.putExtra("topic_id", topicId);
replyIntent.putExtra("currentAccount", currentAccount);
PendingIntent replyPendingIntent = PendingIntent.getBroadcast(ApplicationLoader.applicationContext, internalId, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
RemoteInput remoteInputWear = new RemoteInput.Builder(EXTRA_VOICE_REPLY).setLabel(LocaleController.getString("Reply", R.string.Reply)).build();
@ -4211,6 +4268,10 @@ public class NotificationsController extends BaseController {
int rowsMid = 0;
for (int a = messageObjects.size() - 1; a >= 0; a--) {
MessageObject messageObject = messageObjects.get(a);
int messageTopicId = MessageObject.getTopicId(messageObject.messageOwner);
if (topicId != messageTopicId) {
continue;
}
String message = getShortStringForMessage(messageObject, senderName, preview);
if (dialogId == selfUserId) {
senderName[0] = name;
@ -4247,8 +4308,8 @@ public class NotificationsController extends BaseController {
} else {
uid = dialogId;
}
Person person = personCache.get(uid);
String personName = "";
Person person = personCache.get(uid + ((long) topicId << 16));
CharSequence personName = "";
if (senderName[0] == null) {
if (waitingForPasscode) {
if (DialogObject.isChatDialog(dialogId)) {
@ -4266,6 +4327,7 @@ public class NotificationsController extends BaseController {
} else {
personName = senderName[0];
}
if (person == null || !TextUtils.equals(person.getName(), personName)) {
Person.Builder personBuilder = new Person.Builder().setName(personName);
if (preview[0] && !DialogObject.isEncryptedDialog(dialogId) && Build.VERSION.SDK_INT >= 28) {
@ -4291,10 +4353,11 @@ public class NotificationsController extends BaseController {
personCache.put(uid, person);
}
if (!DialogObject.isEncryptedDialog(dialogId)) {
boolean setPhoto = false;
if (preview[0] && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && !((ActivityManager) ApplicationLoader.applicationContext.getSystemService(Context.ACTIVITY_SERVICE)).isLowRamDevice()) {
if (!waitingForPasscode && !messageObject.isSecretMedia() && (messageObject.type == 1 || messageObject.isSticker())) {
if (!waitingForPasscode && !messageObject.isSecretMedia() && (messageObject.type == MessageObject.TYPE_PHOTO || messageObject.isSticker())) {
File attach = getFileLoader().getPathToMessage(messageObject.messageOwner);
NotificationCompat.MessagingStyle.Message msg = new NotificationCompat.MessagingStyle.Message(message, ((long) messageObject.messageOwner.date) * 1000L, person);
String mimeType = messageObject.isSticker() ? "image/webp" : "image/jpeg";
@ -4376,6 +4439,9 @@ public class NotificationsController extends BaseController {
} else {
intent.putExtra("chatId", -dialogId);
}
if (topicId != 0) {
intent.putExtra("topicId", topicId);
}
intent.putExtra("currentAccount", currentAccount);
PendingIntent contentIntent = PendingIntent.getActivity(ApplicationLoader.applicationContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
@ -4489,7 +4555,7 @@ public class NotificationsController extends BaseController {
if (Build.VERSION.SDK_INT >= 26) {
setNotificationChannel(mainNotification, builder, useSummaryNotification);
}
holders.add(new NotificationHolder(internalId, dialogId, name, user, chat, builder));
holders.add(new NotificationHolder(internalId, dialogId, topicId, name, user, chat, builder));
wearNotificationsIds.put(dialogId, internalId);
}
@ -4501,7 +4567,7 @@ public class NotificationsController extends BaseController {
notificationManager.notify(notificationId, mainNotification);
} catch (SecurityException e) {
FileLog.e(e);
resetNotificationSound(notificationBuilder, lastDialogId, chatName, vibrationPattern, ledColor, sound, importance, isDefault, isInApp, isSilent, chatType);
resetNotificationSound(notificationBuilder, lastDialogId, lastTopicId, chatName, vibrationPattern, ledColor, sound, importance, isDefault, isInApp, isSilent, chatType);
}
} else {
if (openedInBubbleDialogs.isEmpty()) {
@ -4616,30 +4682,32 @@ public class NotificationsController extends BaseController {
public static final int SETTING_MUTE_UNMUTE = 4;
public static final int SETTING_MUTE_CUSTOM = 5;
public void clearDialogNotificationsSettings(long did) {
public void clearDialogNotificationsSettings(long did, int topicId) {
SharedPreferences preferences = getAccountInstance().getNotificationsSettings();
SharedPreferences.Editor editor = preferences.edit();
editor.remove("notify2_" + did).remove("custom_" + did);
String prefKey = NotificationsController.getSharedPrefKey(did, topicId);
editor.remove("notify2_" + prefKey).remove("custom_" + prefKey);
getMessagesStorage().setDialogFlags(did, 0);
TLRPC.Dialog dialog = getMessagesController().dialogs_dict.get(did);
if (dialog != null) {
dialog.notify_settings = new TLRPC.TL_peerNotifySettings();
}
editor.commit();
getNotificationsController().updateServerNotificationsSettings(did, true);
getNotificationsController().updateServerNotificationsSettings(did, topicId,true);
}
public void setDialogNotificationsSettings(long dialog_id, int setting) {
public void setDialogNotificationsSettings(long dialog_id, int topicId, int setting) {
SharedPreferences preferences = getAccountInstance().getNotificationsSettings();
SharedPreferences.Editor editor = preferences.edit();
TLRPC.Dialog dialog = MessagesController.getInstance(UserConfig.selectedAccount).dialogs_dict.get(dialog_id);
if (setting == SETTING_MUTE_UNMUTE) {
boolean defaultEnabled = isGlobalNotificationsEnabled(dialog_id);
if (defaultEnabled) {
editor.remove("notify2_" + dialog_id);
editor.remove("notify2_" + NotificationsController.getSharedPrefKey(dialog_id, topicId));
} else {
editor.putInt("notify2_" + dialog_id, 0);
editor.putInt("notify2_" + NotificationsController.getSharedPrefKey(dialog_id, topicId), 0);
}
//TODO topic
getMessagesStorage().setDialogFlags(dialog_id, 0);
if (dialog != null) {
dialog.notify_settings = new TLRPC.TL_peerNotifySettings();
@ -4657,11 +4725,11 @@ public class NotificationsController extends BaseController {
}
long flags;
if (setting == SETTING_MUTE_FOREVER) {
editor.putInt("notify2_" + dialog_id, 2);
editor.putInt("notify2_" + NotificationsController.getSharedPrefKey(dialog_id, topicId), 2);
flags = 1;
} else {
editor.putInt("notify2_" + dialog_id, 3);
editor.putInt("notifyuntil_" + dialog_id, untilTime);
editor.putInt("notify2_" + NotificationsController.getSharedPrefKey(dialog_id, topicId), 3);
editor.putInt("notifyuntil_" + NotificationsController.getSharedPrefKey(dialog_id, topicId), untilTime);
flags = ((long) untilTime << 32) | 1;
}
NotificationsController.getInstance(UserConfig.selectedAccount).removeNotificationsForDialog(dialog_id);
@ -4672,14 +4740,14 @@ public class NotificationsController extends BaseController {
}
}
editor.commit();
updateServerNotificationsSettings(dialog_id);
updateServerNotificationsSettings(dialog_id, topicId);
}
public void updateServerNotificationsSettings(long dialog_id) {
updateServerNotificationsSettings(dialog_id, true);
public void updateServerNotificationsSettings(long dialog_id, int topicId) {
updateServerNotificationsSettings(dialog_id, topicId, true);
}
public void updateServerNotificationsSettings(long dialogId, boolean post) {
public void updateServerNotificationsSettings(long dialogId, int topicId, boolean post) {
if (post) {
getNotificationCenter().postNotificationName(NotificationCenter.notificationsSettingsUpdated);
}
@ -4691,23 +4759,23 @@ public class NotificationsController extends BaseController {
req.settings = new TLRPC.TL_inputPeerNotifySettings();
req.settings.flags |= 1;
req.settings.show_previews = preferences.getBoolean("content_preview_" + dialogId, true);
req.settings.show_previews = preferences.getBoolean("content_preview_" + NotificationsController.getSharedPrefKey(dialogId, topicId), true);
req.settings.flags |= 2;
req.settings.silent = preferences.getBoolean("silent_" + dialogId, false);
req.settings.silent = preferences.getBoolean("silent_" + NotificationsController.getSharedPrefKey(dialogId, topicId), false);
int mute_type = preferences.getInt("notify2_" + dialogId, -1);
int mute_type = preferences.getInt("notify2_" + NotificationsController.getSharedPrefKey(dialogId, topicId), -1);
if (mute_type != -1) {
req.settings.flags |= 4;
if (mute_type == 3) {
req.settings.mute_until = preferences.getInt("notifyuntil_" + dialogId, 0);
req.settings.mute_until = preferences.getInt("notifyuntil_" + NotificationsController.getSharedPrefKey(dialogId, topicId), 0);
} else {
req.settings.mute_until = mute_type != 2 ? 0 : Integer.MAX_VALUE;
}
}
long soundDocumentId = preferences.getLong("sound_document_id_" + dialogId, 0);
String soundPath = preferences.getString("sound_path_" + dialogId, null);
long soundDocumentId = preferences.getLong("sound_document_id_" + NotificationsController.getSharedPrefKey(dialogId, topicId), 0);
String soundPath = preferences.getString("sound_path_" + NotificationsController.getSharedPrefKey(dialogId, topicId), null);
req.settings.flags |= 8;
if (soundDocumentId != 0) {
TLRPC.TL_notificationSoundRingtone ringtoneSound = new TLRPC.TL_notificationSoundRingtone();
@ -4718,19 +4786,25 @@ public class NotificationsController extends BaseController {
req.settings.sound = new TLRPC.TL_notificationSoundNone();
} else {
TLRPC.TL_notificationSoundLocal localSound = new TLRPC.TL_notificationSoundLocal();
localSound.title = preferences.getString("sound_" + dialogId, null);
localSound.title = preferences.getString("sound_" + NotificationsController.getSharedPrefKey(dialogId, topicId), null);
localSound.data = soundPath;
req.settings.sound = localSound;
}
} else {
req.settings.sound = new TLRPC.TL_notificationSoundDefault();
}
if (topicId != 0) {
TLRPC.TL_inputNotifyForumTopic topicPeer = new TLRPC.TL_inputNotifyForumTopic();
topicPeer.peer = getMessagesController().getInputPeer(dialogId);
topicPeer.top_msg_id = topicId;
req.peer = topicPeer;
} else {
req.peer = new TLRPC.TL_inputNotifyPeer();
((TLRPC.TL_inputNotifyPeer) req.peer).peer = getMessagesController().getInputPeer(dialogId);
}
req.peer = new TLRPC.TL_inputNotifyPeer();
((TLRPC.TL_inputNotifyPeer) req.peer).peer = getMessagesController().getInputPeer(dialogId);
getConnectionsManager().sendRequest(req, (response, error) -> {
// FileLog.d("updateServerNotificationsSettings " + dialogId + " " + topicId + " error = " + error);
});
}
@ -4842,25 +4916,69 @@ public class NotificationsController extends BaseController {
}
}
public void muteDialog(long dialog_id, boolean mute) {
public void muteDialog(long dialog_id, int topicId, boolean mute) {
if (mute) {
NotificationsController.getInstance(currentAccount).muteUntil(dialog_id, Integer.MAX_VALUE);
NotificationsController.getInstance(currentAccount).muteUntil(dialog_id, topicId, Integer.MAX_VALUE);
} else {
boolean defaultEnabled = NotificationsController.getInstance(currentAccount).isGlobalNotificationsEnabled(dialog_id);
boolean override = topicId != 0;
SharedPreferences preferences = MessagesController.getNotificationsSettings(currentAccount);
SharedPreferences.Editor editor = preferences.edit();
if (defaultEnabled) {
editor.remove("notify2_" + dialog_id);
if (defaultEnabled && !override) {
editor.remove("notify2_" + getSharedPrefKey(dialog_id, topicId));
} else {
editor.putInt("notify2_" + dialog_id, 0);
editor.putInt("notify2_" + getSharedPrefKey(dialog_id, topicId), 0);
}
if (topicId == 0) {
getMessagesStorage().setDialogFlags(dialog_id, 0);
TLRPC.Dialog dialog = getMessagesController().dialogs_dict.get(dialog_id);
if (dialog != null) {
dialog.notify_settings = new TLRPC.TL_peerNotifySettings();
}
}
getMessagesStorage().setDialogFlags(dialog_id, 0);
editor.apply();
TLRPC.Dialog dialog = getMessagesController().dialogs_dict.get(dialog_id);
if (dialog != null) {
dialog.notify_settings = new TLRPC.TL_peerNotifySettings();
updateServerNotificationsSettings(dialog_id, topicId);
}
}
public NotificationsSettingsFacade getNotificationsSettingsFacade() {
return dialogsNotificationsFacade;
}
public void loadTopicsNotificationsExceptions(long dialogId, Consumer<HashSet<Integer>> consumer) {
getMessagesStorage().getStorageQueue().postRunnable(() -> {
HashSet<Integer> topics = new HashSet<>();
SharedPreferences preferences = MessagesController.getNotificationsSettings(currentAccount);
Map<String, ?> values = preferences.getAll();
for (Map.Entry<String, ?> entry : values.entrySet()) {
String key = entry.getKey();
if (key.startsWith("notify2_" + dialogId)) {
key = key.replace("notify2_" + dialogId, "");
int topicId = Utilities.parseInt(key);
if (topicId != 0) {
if (getMessagesController().isDialogMuted(dialogId, topicId) != getMessagesController().isDialogMuted(dialogId, 0)) {
topics.add(topicId);
}
}
}
}
updateServerNotificationsSettings(dialog_id);
AndroidUtilities.runOnUIThread(() -> {
if (consumer != null) {
consumer.accept(topics);
}
});
});
}
private static class DialogKey {
final long dialogId;
final int topicId;
private DialogKey(long dialogId, int topicId) {
this.dialogId = dialogId;
this.topicId = topicId;
}
}
}

View File

@ -8,6 +8,9 @@
package org.telegram.messenger;
import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
import static android.app.NotificationManager.EXTRA_NOTIFICATION_CHANNEL_ID;
import android.annotation.TargetApi;
import android.content.BroadcastReceiver;
import android.content.Context;
@ -16,9 +19,6 @@ import android.content.SharedPreferences;
import android.os.SystemClock;
import android.text.TextUtils;
import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
import static android.app.NotificationManager.EXTRA_NOTIFICATION_CHANNEL_ID;
@TargetApi(28)
public class NotificationsDisabledReceiver extends BroadcastReceiver {
@ -86,6 +86,8 @@ public class NotificationsDisabledReceiver extends BroadcastReceiver {
if (dialogId == 0) {
return;
}
int topicId = 0;
String key = NotificationsController.getSharedPrefKey(dialogId, topicId);
String currentChannel = preferences.getString("org.telegram.key" + dialogId, null);
if (!channelId.equals(currentChannel)) {
return;
@ -94,12 +96,12 @@ public class NotificationsDisabledReceiver extends BroadcastReceiver {
FileLog.d("apply channel " + channelId + " state");
}
SharedPreferences.Editor editor = preferences.edit();
editor.putInt("notify2_" + dialogId, state ? 2 : 0);
editor.putInt("notify2_" + key, state ? 2 : 0);
if (!state) {
editor.remove("notifyuntil_" + dialogId);
editor.remove("notifyuntil_" + key);
}
editor.commit();
AccountInstance.getInstance(account).getNotificationsController().updateServerNotificationsSettings(dialogId, true);
AccountInstance.getInstance(account).getNotificationsController().updateServerNotificationsSettings(dialogId, 0, true);
}
AccountInstance.getInstance(account).getConnectionsManager().resumeNetworkMaybe();
}

View File

@ -0,0 +1,258 @@
package org.telegram.messenger;
import static org.telegram.messenger.NotificationsController.TYPE_PRIVATE;
import android.content.SharedPreferences;
import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.TLRPC;
public class NotificationsSettingsFacade {
public final static String PROPERTY_NOTIFY = "notify2_";
public final static String PROPERTY_CUSTOM = "custom_";
public final static String PROPERTY_NOTIFY_UNTIL = "notifyuntil_";
public final static String PROPERTY_CONTENT_PREVIEW = "content_preview_";
public final static String PROPERTY_SILENT = "silent_";
private final int currentAccount;
public NotificationsSettingsFacade(int currentAccount) {
this.currentAccount = currentAccount;
}
public boolean isDefault(long dialogId, int topicId) {
String key = NotificationsController.getSharedPrefKey(dialogId, topicId);
return false;
}
public void clearPreference(long dialogId, int topicId) {
String key = NotificationsController.getSharedPrefKey(dialogId, topicId);
getPreferences().edit()
.remove(PROPERTY_NOTIFY + key)
.remove(PROPERTY_CUSTOM + key)
.remove(PROPERTY_NOTIFY_UNTIL + key)
.remove(PROPERTY_CONTENT_PREVIEW + key)
.remove(PROPERTY_SILENT + key)
.apply();
}
public int getProperty(String property, long dialogId, int topicId, int defaultValue) {
String key = NotificationsController.getSharedPrefKey(dialogId, topicId);
if (getPreferences().contains(property + key)) {
return getPreferences().getInt(property + key, defaultValue);
}
key = NotificationsController.getSharedPrefKey(dialogId, 0);
return getPreferences().getInt(property + key, defaultValue);
}
public long getProperty(String property, long dialogId, int topicId, long defaultValue) {
String key = NotificationsController.getSharedPrefKey(dialogId, topicId);
if (getPreferences().contains(property + key)) {
return getPreferences().getLong(property + key, defaultValue);
}
key = NotificationsController.getSharedPrefKey(dialogId, 0);
return getPreferences().getLong(property + key, defaultValue);
}
public boolean getProperty(String property, long dialogId, int topicId, boolean defaultValue) {
String key = NotificationsController.getSharedPrefKey(dialogId, topicId);
if (getPreferences().contains(property + key)) {
return getPreferences().getBoolean(property + key, defaultValue);
}
key = NotificationsController.getSharedPrefKey(dialogId, 0);
return getPreferences().getBoolean(property + key, defaultValue);
}
public String getPropertyString(String property, long dialogId, int topicId, String defaultValue) {
String key = NotificationsController.getSharedPrefKey(dialogId, topicId);
if (getPreferences().contains(property + key)) {
return getPreferences().getString(property + key, defaultValue);
}
key = NotificationsController.getSharedPrefKey(dialogId, 0);
return getPreferences().getString(property + key, defaultValue);
}
public void removeProperty(String property, long dialogId, int topicId) {
String key = NotificationsController.getSharedPrefKey(dialogId, topicId);
getPreferences().edit().remove(property + key).apply();
}
private SharedPreferences getPreferences() {
return MessagesController.getNotificationsSettings(currentAccount);
}
public void applyDialogNotificationsSettings(long dialogId, int topicId, TLRPC.PeerNotifySettings notify_settings) {
if (notify_settings == null) {
return;
}
Utilities.globalQueue.postRunnable(() -> {
String key = NotificationsController.getSharedPrefKey(dialogId, topicId);
MessagesController messagesController = MessagesController.getInstance(currentAccount);
ConnectionsManager connectionsManager = ConnectionsManager.getInstance(currentAccount);
MessagesStorage messagesStorage = MessagesStorage.getInstance(currentAccount);
NotificationsController notificationsController = NotificationsController.getInstance(currentAccount);
int currentValue = getPreferences().getInt(PROPERTY_NOTIFY + key, -1);
int currentValue2 = getPreferences().getInt(PROPERTY_NOTIFY_UNTIL + key, 0);
SharedPreferences.Editor editor = getPreferences().edit();
boolean updated = false;
if ((notify_settings.flags & 2) != 0) {
editor.putBoolean(PROPERTY_SILENT + key, notify_settings.silent);
} else {
editor.remove(PROPERTY_SILENT + key);
}
TLRPC.Dialog dialog = null;
if (topicId == 0) {
dialog = messagesController.dialogs_dict.get(dialogId);
}
if (dialog != null) {
dialog.notify_settings = notify_settings;
}
if ((notify_settings.flags & 4) != 0) {
if (notify_settings.mute_until > connectionsManager.getCurrentTime()) {
int until = 0;
if (notify_settings.mute_until > connectionsManager.getCurrentTime() + 60 * 60 * 24 * 365) {
if (currentValue != 2) {
updated = true;
editor.putInt(PROPERTY_NOTIFY + key, 2);
if (dialog != null) {
dialog.notify_settings.mute_until = Integer.MAX_VALUE;
}
}
} else {
if (currentValue != 3 || currentValue2 != notify_settings.mute_until) {
updated = true;
editor.putInt(PROPERTY_NOTIFY + key, 3);
editor.putInt(PROPERTY_NOTIFY_UNTIL + key, notify_settings.mute_until);
if (dialog != null) {
dialog.notify_settings.mute_until = until;
}
}
until = notify_settings.mute_until;
}
if (topicId == 0) {
messagesStorage.setDialogFlags(dialogId, ((long) until << 32) | 1);
notificationsController.removeNotificationsForDialog(dialogId);
}
} else {
if (currentValue != 0 && currentValue != 1) {
updated = true;
if (dialog != null) {
dialog.notify_settings.mute_until = 0;
}
editor.putInt(PROPERTY_NOTIFY + key, 0);
}
if (topicId == 0) {
messagesStorage.setDialogFlags(dialogId, 0);
}
}
} else {
if (currentValue != -1) {
updated = true;
if (dialog != null) {
dialog.notify_settings.mute_until = 0;
}
editor.remove(PROPERTY_NOTIFY + key);
}
if (topicId == 0) {
messagesStorage.setDialogFlags(dialogId, 0);
}
}
applySoundSettings(notify_settings.android_sound, editor, dialogId, topicId, 0, false);
editor.apply();
if (updated) {
AndroidUtilities.runOnUIThread(() -> {
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.notificationsSettingsUpdated);
});
}
});
}
public void applySoundSettings(TLRPC.NotificationSound settings, SharedPreferences.Editor editor, long dialogId, int topicId, int globalType, boolean serverUpdate) {
if (settings == null) {
return;
}
String soundPref;
String soundPathPref;
String soundDocPref;
if (dialogId != 0) {
String key = NotificationsController.getSharedPrefKey(dialogId, topicId);
soundPref = "sound_" + key;
soundPathPref = "sound_path_" + key;
soundDocPref = "sound_document_id_" + key;
} else {
if (globalType == NotificationsController.TYPE_GROUP) {
soundPref = "GroupSound";
soundDocPref = "GroupSoundDocId";
soundPathPref = "GroupSoundPath";
} else if (globalType == TYPE_PRIVATE) {
soundPref = "GlobalSound";
soundDocPref = "GlobalSoundDocId";
soundPathPref = "GlobalSoundPath";
} else {
soundPref = "ChannelSound";
soundDocPref = "ChannelSoundDocId";
soundPathPref = "ChannelSoundPath";
}
}
if (settings instanceof TLRPC.TL_notificationSoundDefault) {
editor.putString(soundPref, "Default");
editor.putString(soundPathPref, "Default");
editor.remove(soundDocPref);
} else if (settings instanceof TLRPC.TL_notificationSoundNone) {
editor.putString(soundPref, "NoSound");
editor.putString(soundPathPref, "NoSound");
editor.remove(soundDocPref);
} else if (settings instanceof TLRPC.TL_notificationSoundLocal) {
TLRPC.TL_notificationSoundLocal localSound = (TLRPC.TL_notificationSoundLocal) settings;
editor.putString(soundPref, localSound.title);
editor.putString(soundPathPref, localSound.data);
editor.remove(soundDocPref);
} else if (settings instanceof TLRPC.TL_notificationSoundRingtone) {
TLRPC.TL_notificationSoundRingtone soundRingtone = (TLRPC.TL_notificationSoundRingtone) settings;
editor.putLong(soundDocPref, soundRingtone.id);
MediaDataController.getInstance(currentAccount).checkRingtones();
if (serverUpdate && dialogId != 0) {
editor.putBoolean("custom_" + dialogId, true);
}
MediaDataController.getInstance(currentAccount).ringtoneDataStore.getDocument(soundRingtone.id);
}
}
public void setSettingsForDialog(TLRPC.Dialog dialog, TLRPC.PeerNotifySettings notify_settings) {
SharedPreferences.Editor editor = getPreferences().edit();
long dialogId = MessageObject.getPeerId(dialog.peer);
if ((dialog.notify_settings.flags & 2) != 0) {
editor.putBoolean(PROPERTY_SILENT + dialogId, dialog.notify_settings.silent);
} else {
editor.remove(PROPERTY_SILENT + dialogId);
}
ConnectionsManager connectionsManager = ConnectionsManager.getInstance(currentAccount);
if ((dialog.notify_settings.flags & 4) != 0) {
if (dialog.notify_settings.mute_until > connectionsManager.getCurrentTime()) {
if (dialog.notify_settings.mute_until > connectionsManager.getCurrentTime() + 60 * 60 * 24 * 365) {
editor.putInt(PROPERTY_NOTIFY + dialogId, 2);
dialog.notify_settings.mute_until = Integer.MAX_VALUE;
} else {
editor.putInt(PROPERTY_NOTIFY + dialogId, 3);
editor.putInt(PROPERTY_NOTIFY_UNTIL + dialogId, dialog.notify_settings.mute_until);
}
} else {
editor.putInt(PROPERTY_NOTIFY + dialogId, 0);
}
} else {
editor.remove(PROPERTY_NOTIFY + dialogId);
}
editor.apply();
}
}

View File

@ -264,6 +264,8 @@ public class PushListenerController {
long chat_id;
long user_id;
long dialogId = 0;
int topicId = 0;
boolean scheduled;
if (custom.has("channel_id")) {
channel_id = custom.getLong("channel_id");
@ -283,6 +285,9 @@ public class PushListenerController {
} else {
chat_id = 0;
}
if (custom.has("topic_id")) {
topicId =custom.getInt("topic_id");
}
if (custom.has("encryption_id")) {
dialogId = DialogObject.makeEncryptedDialogId(custom.getInt("encryption_id"));
}
@ -350,7 +355,7 @@ public class PushListenerController {
deletedMessages.put(-channel_id, ids);
NotificationsController.getInstance(currentAccount).removeDeletedMessagesFromNotifications(deletedMessages, true);
MessagesController.getInstance(currentAccount).checkUnreadReactions(dialogId, sparseBooleanArray);
MessagesController.getInstance(currentAccount).checkUnreadReactions(dialogId, topicId, sparseBooleanArray);
if (BuildVars.LOGS_ENABLED) {
FileLog.d(tag + " received " + loc_key + " for dialogId = " + dialogId + " mids = " + TextUtils.join(",", ids));
}
@ -1147,6 +1152,11 @@ public class PushListenerController {
messageOwner.from_scheduled = scheduled;
MessageObject messageObject = new MessageObject(currentAccount, messageOwner, messageText, name, userName, localMessage, channel, supergroup, edited);
if (topicId != 0) {
messageObject.messageOwner.reply_to = new TLRPC.TL_messageReplyHeader();
messageObject.messageOwner.reply_to.forum_topic = true;
messageObject.messageOwner.reply_to.reply_to_top_id = topicId;
}
messageObject.isReactionPush = loc_key.startsWith("REACT_") || loc_key.startsWith("CHAT_REACT_");
ArrayList<MessageObject> arrayList = new ArrayList<>();
arrayList.add(messageObject);

View File

@ -154,7 +154,7 @@ public class SecretChatHelper extends BaseController {
ArrayList<TLRPC.Message> arr = new ArrayList<>();
arr.add(newMsg);
getMessagesStorage().putMessages(arr, false, true, true, 0, false);
getMessagesStorage().putMessages(arr, false, true, true, 0, false, 0);
return newMsg;
}
@ -534,7 +534,7 @@ public class SecretChatHelper extends BaseController {
ImageLoader.getInstance().replaceImageInCache(fileName, fileName2, ImageLocation.getForPhoto(size, newMsg.media.photo), true);
ArrayList<TLRPC.Message> arr = new ArrayList<>();
arr.add(newMsg);
getMessagesStorage().putMessages(arr, false, true, false, 0, false);
getMessagesStorage().putMessages(arr, false, true, false, 0, false, 0);
//getMessagesStorage().putSentFile(originalPath, newMsg.media.photo, 3);
} else if (newMsg.media instanceof TLRPC.TL_messageMediaDocument && newMsg.media.document != null) {
@ -568,7 +568,7 @@ public class SecretChatHelper extends BaseController {
ArrayList<TLRPC.Message> arr = new ArrayList<>();
arr.add(newMsg);
getMessagesStorage().putMessages(arr, false, true, false, 0, false);
getMessagesStorage().putMessages(arr, false, true, false, 0, false, 0);
}
}
}

View File

@ -83,6 +83,7 @@ import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
@ -100,6 +101,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
private LongSparseArray<Integer> uploadingMessagesIdDialogs = new LongSparseArray<>();
private HashMap<String, MessageObject> waitingForLocation = new HashMap<>();
private HashMap<String, Boolean> waitingForCallback = new HashMap<>();
private HashMap<String, List<String>> waitingForCallbackMap = new HashMap<>();
private HashMap<String, byte[]> waitingForVote = new HashMap<>();
private LongSparseArray<Long> voteSendTime = new LongSparseArray();
private HashMap<String, ImportingHistory> importingHistoryFiles = new HashMap<>();
@ -1057,7 +1059,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
ArrayList<TLRPC.Message> messages = new ArrayList<>();
messages.add(obj.messageOwner);
getMessagesStorage().putMessages(messages, false, true, false, 0, obj.scheduled);
getMessagesStorage().putMessages(messages, false, true, false, 0, obj.scheduled, 0);
break;
}
}
@ -1068,7 +1070,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
ArrayList<TLRPC.Message> messages = new ArrayList<>();
messages.add(message.obj.messageOwner);
getMessagesStorage().putMessages(messages, false, true, false, 0, message.obj.scheduled);
getMessagesStorage().putMessages(messages, false, true, false, 0, message.obj.scheduled, 0);
break;
}
}
@ -1140,7 +1142,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
messageObject.messageOwner.attachPath = cacheFile.toString();
ArrayList<TLRPC.Message> messages = new ArrayList<>();
messages.add(messageObject.messageOwner);
getMessagesStorage().putMessages(messages, false, true, false, 0, messageObject.scheduled);
getMessagesStorage().putMessages(messages, false, true, false, 0, messageObject.scheduled, 0);
getNotificationCenter().postNotificationName(NotificationCenter.updateMessageMedia, messageObject.messageOwner);
message.photoSize = photo.sizes.get(photo.sizes.size() - 1);
message.locationParent = photo;
@ -1189,7 +1191,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
}
ArrayList<TLRPC.Message> messages = new ArrayList<>();
messages.add(messageObject.messageOwner);
getMessagesStorage().putMessages(messages, false, true, false, 0, messageObject.scheduled);
getMessagesStorage().putMessages(messages, false, true, false, 0, messageObject.scheduled, 0);
message.performMediaUpload = true;
performSendDelayedMessage(message);
getNotificationCenter().postNotificationName(NotificationCenter.updateMessageMedia, message.obj.messageOwner);
@ -1243,7 +1245,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
object.type = -1;
object.setType();
object.caption = null;
if (object.type != 0) {
if (object.type != MessageObject.TYPE_TEXT) {
object.generateCaption();
} else {
object.resetLayout();
@ -1252,7 +1254,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
ArrayList<TLRPC.Message> arr = new ArrayList<>();
arr.add(object.messageOwner);
getMessagesStorage().putMessages(arr, false, true, false, 0, object.scheduled);
getMessagesStorage().putMessages(arr, false, true, false, 0, object.scheduled, 0);
ArrayList<MessageObject> arrayList = new ArrayList<>();
arrayList.add(object);
@ -1330,7 +1332,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
TLRPC.TL_messages_messages messagesRes = new TLRPC.TL_messages_messages();
messagesRes.messages.add(prevMessage.messageOwner);
getMessagesStorage().putMessages(messagesRes, message.peer, -2, 0, false, scheduled);
getMessagesStorage().putMessages(messagesRes, message.peer, -2, 0, false, scheduled, 0);
}
if (!checkReadyToSendGroups.contains(message)) {
checkReadyToSendGroups.add(message);
@ -1544,7 +1546,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
getNotificationCenter().postNotificationName(NotificationCenter.dialogsNeedReload);
ArrayList<TLRPC.Message> arr = new ArrayList<>();
arr.add(message);
getMessagesStorage().putMessages(arr, false, true, false, 0, false);
getMessagesStorage().putMessages(arr, false, true, false, 0, false, 0);
performSendMessageRequest(req, newMsgObj, null, null, null, null, false);
}
@ -1655,6 +1657,10 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
}
public int sendMessage(ArrayList<MessageObject> messages, final long peer, boolean forwardFromMyName, boolean hideCaption, boolean notify, int scheduleDate) {
return sendMessage(messages, peer, forwardFromMyName, hideCaption, notify, scheduleDate, null);
}
public int sendMessage(ArrayList<MessageObject> messages, final long peer, boolean forwardFromMyName, boolean hideCaption, boolean notify, int scheduleDate, MessageObject replyToTopMsg) {
if (messages == null || messages.isEmpty()) {
return 0;
}
@ -1718,9 +1724,9 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
for (int a = 0; a < messages.size(); a++) {
MessageObject msgObj = messages.get(a);
if (msgObj.getId() <= 0 || msgObj.needDrawBluredPreview()) {
if (msgObj.type == 0 && !TextUtils.isEmpty(msgObj.messageText)) {
if (msgObj.type == MessageObject.TYPE_TEXT && !TextUtils.isEmpty(msgObj.messageText)) {
TLRPC.WebPage webPage = msgObj.messageOwner.media != null ? msgObj.messageOwner.media.webpage : null;
sendMessage(msgObj.messageText.toString(), peer, null, null, webPage, webPage != null, msgObj.messageOwner.entities, null, null, notify, scheduleDate, null, false);
sendMessage(msgObj.messageText.toString(), peer, null, replyToTopMsg, webPage, webPage != null, msgObj.messageOwner.entities, null, null, notify, scheduleDate, null, false);
}
continue;
}
@ -1990,8 +1996,17 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
FileLog.d("forward message user_id = " + inputPeer.user_id + " chat_id = " + inputPeer.chat_id + " channel_id = " + inputPeer.channel_id + " access_hash = " + inputPeer.access_hash);
}
if (replyToTopMsg != null) {
newMsg.reply_to = new TLRPC.TL_messageReplyHeader();
newMsg.reply_to.reply_to_msg_id = replyToTopMsg.getId();
if (replyToTopMsg.isTopicMainMessage) {
newMsg.reply_to.forum_topic = true;
newMsg.reply_to.flags |= 8;
}
}
if (arr.size() == 100 || a == messages.size() - 1 || a != messages.size() - 1 && messages.get(a + 1).getDialogId() != msgObj.getDialogId()) {
getMessagesStorage().putMessages(new ArrayList<>(arr), false, true, false, 0, scheduleDate != 0);
getMessagesStorage().putMessages(new ArrayList<>(arr), false, true, false, 0, scheduleDate != 0, 0);
getMessagesController().updateInterfaceWithMessages(peer, objArr, scheduleDate != 0);
getNotificationCenter().postNotificationName(NotificationCenter.dialogsNeedReload);
getUserConfig().saveConfig(false);
@ -1999,6 +2014,10 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
final TLRPC.TL_messages_forwardMessages req = new TLRPC.TL_messages_forwardMessages();
req.to_peer = inputPeer;
req.silent = !notify || MessagesController.getNotificationsSettings(currentAccount).getBoolean("silent_" + peer, false);
if (replyToTopMsg != null) {
req.top_msg_id = replyToTopMsg.getId();
req.flags |= 512;
}
if (scheduleDate != 0) {
req.schedule_date = scheduleDate;
req.flags |= 1024;
@ -2108,7 +2127,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
messageIds.add(oldId);
getMessagesController().deleteMessages(messageIds, null, null, newMsgObj1.dialog_id, false, true);
getMessagesStorage().getStorageQueue().postRunnable(() -> {
getMessagesStorage().putMessages(sentMessages, true, false, false, 0, false);
getMessagesStorage().putMessages(sentMessages, true, false, false, 0, false, 0);
AndroidUtilities.runOnUIThread(() -> {
ArrayList<MessageObject> messageObjects = new ArrayList<>();
messageObjects.add(new MessageObject(msgObj.currentAccount, msgObj.messageOwner, true, true));
@ -2122,7 +2141,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
} else {
getMessagesStorage().getStorageQueue().postRunnable(() -> {
getMessagesStorage().updateMessageStateAndId(newMsgObj1.random_id, MessageObject.getPeerId(peer_id), oldId, newMsgObj1.id, 0, false, scheduleDate != 0 ? 1 : 0);
getMessagesStorage().putMessages(sentMessages, true, false, false, 0, scheduleDate != 0);
getMessagesStorage().putMessages(sentMessages, true, false, false, 0, scheduleDate != 0, 0);
AndroidUtilities.runOnUIThread(() -> {
newMsgObj1.send_state = MessageObject.MESSAGE_SEND_STATE_SENT;
getMediaDataController().increasePeerRaiting(peer);
@ -2316,7 +2335,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
newMsg.attachPath = "";
}
newMsg.local_id = 0;
if ((messageObject.type == 3 || videoEditedInfo != null || messageObject.type == MessageObject.TYPE_VOICE) && !TextUtils.isEmpty(newMsg.attachPath)) {
if ((messageObject.type == MessageObject.TYPE_VIDEO || videoEditedInfo != null || messageObject.type == MessageObject.TYPE_VOICE) && !TextUtils.isEmpty(newMsg.attachPath)) {
messageObject.attachPathExists = true;
}
if (messageObject.videoEditedInfo != null && videoEditedInfo == null) {
@ -2355,7 +2374,8 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
ArrayList<TLRPC.Message> arr = new ArrayList<>();
arr.add(newMsg);
getMessagesStorage().putMessages(arr, false, true, false, 0, messageObject.scheduled);
getMessagesStorage().putMessages(arr, false, true, false, 0, messageObject.scheduled, MessageObject.getTopicId(newMsg));
getMessagesController().getTopicsController().processEditedMessage(newMsg);
messageObject.type = -1;
messageObject.setType();
@ -2653,6 +2673,12 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
final String key = dialogId + "_" + msgId + "_" + Utilities.bytesToHex(data) + "_" + 0;
waitingForCallback.put(key, true);
List<String> keys = waitingForCallbackMap.get(dialogId + "_" + msgId);
if (keys == null) {
waitingForCallbackMap.put(dialogId + "_" + msgId, keys = new ArrayList<>());
}
keys.add(key);
if (DialogObject.isUserDialog(dialogId)) {
TLRPC.User user = getMessagesController().getUser(dialogId);
if (user == null) {
@ -2679,11 +2705,26 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
req.flags |= 1;
req.data = data;
}
getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> waitingForCallback.remove(key)), ConnectionsManager.RequestFlagFailOnServerErrors);
List<String> finalKeys = keys;
getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> {
waitingForCallback.remove(key);
finalKeys.remove(key);
}), ConnectionsManager.RequestFlagFailOnServerErrors);
getMessagesController().markDialogAsRead(dialogId, msgId, msgId, 0, false, 0, 0, true, 0);
});
}
public void onMessageEdited(TLRPC.Message message) {
if (message != null && message.reply_markup != null) {
List<String> keys = waitingForCallbackMap.remove(message.dialog_id + "_" + message.id);
if (keys != null) {
for (String key : keys) {
waitingForCallback.remove(key);
}
}
}
}
public byte[] isSendingVote(MessageObject messageObject) {
if (messageObject == null) {
return null;
@ -2828,9 +2869,17 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
final String key = messageObject.getDialogId() + "_" + messageObject.getId() + "_" + Utilities.bytesToHex(button.data) + "_" + type;
waitingForCallback.put(key, true);
List<String> keys = waitingForCallbackMap.get(messageObject.getDialogId() + "_" + messageObject.getId());
if (keys == null) {
waitingForCallbackMap.put(messageObject.getDialogId() + "_" + messageObject.getId(), keys = new ArrayList<>());
}
keys.add(key);
TLObject[] request = new TLObject[1];
List<String> finalKeys = keys;
RequestDelegate requestDelegate = (response, error) -> AndroidUtilities.runOnUIThread(() -> {
waitingForCallback.remove(key);
finalKeys.remove(key);
if (cacheFinal && response == null) {
sendCallback(false, messageObject, button, parentFragment);
} else if (response != null) {
@ -3245,29 +3294,33 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
type = 11;
message = retryMessageObject.getDiceEmoji();
caption = "";
} else if (retryMessageObject.type == 0 || retryMessageObject.isAnimatedEmoji()) {
} else if (retryMessageObject.type == MessageObject.TYPE_TEXT || retryMessageObject.isAnimatedEmoji()) {
if (retryMessageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGame) {
//game = retryMessageObject.messageOwner.media.game;
} else {
message = newMsg.message;
}
type = 0;
} else if (retryMessageObject.type == 4) {
} else if (retryMessageObject.type == MessageObject.TYPE_GEO) {
location = newMsg.media;
type = 1;
} else if (retryMessageObject.type == 1) {
} else if (retryMessageObject.type == MessageObject.TYPE_PHOTO) {
photo = (TLRPC.TL_photo) newMsg.media.photo;
if (retryMessageObject.messageOwner.message != null) {
caption = retryMessageObject.messageOwner.message;
}
type = 2;
} else if (retryMessageObject.type == 3 || retryMessageObject.type == 5 || retryMessageObject.videoEditedInfo != null) {
} else if (
retryMessageObject.type == MessageObject.TYPE_VIDEO ||
retryMessageObject.type == MessageObject.TYPE_ROUND_VIDEO ||
retryMessageObject.videoEditedInfo != null
) {
type = 3;
document = (TLRPC.TL_document) newMsg.media.document;
if (retryMessageObject.messageOwner.message != null) {
caption = retryMessageObject.messageOwner.message;
}
} else if (retryMessageObject.type == 12) {
} else if (retryMessageObject.type == MessageObject.TYPE_CONTACT) {
user = new TLRPC.TL_userRequest_old2();
user.phone = newMsg.media.phone_number;
user.first_name = newMsg.media.first_name;
@ -3279,7 +3332,13 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
user.restriction_reason.add(reason);
user.id = newMsg.media.user_id;
type = 6;
} else if (retryMessageObject.type == 8 || retryMessageObject.type == 9 || retryMessageObject.type == MessageObject.TYPE_STICKER || retryMessageObject.type == 14 || retryMessageObject.type == MessageObject.TYPE_ANIMATED_STICKER) {
} else if (
retryMessageObject.type == MessageObject.TYPE_GIF ||
retryMessageObject.type == MessageObject.TYPE_FILE ||
retryMessageObject.type == MessageObject.TYPE_STICKER ||
retryMessageObject.type == MessageObject.TYPE_MUSIC ||
retryMessageObject.type == MessageObject.TYPE_ANIMATED_STICKER
) {
document = (TLRPC.TL_document) newMsg.media.document;
type = 7;
if (retryMessageObject.messageOwner.message != null) {
@ -3595,6 +3654,15 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
if (replyToTopMsg != null && replyToTopMsg != replyToMsg) {
newMsg.reply_to.reply_to_top_id = replyToTopMsg.getId();
newMsg.reply_to.flags |= 2;
if (replyToTopMsg.isTopicMainMessage) {
newMsg.reply_to.forum_topic = true;
newMsg.reply_to.flags |= 8;
}
} else {
if (replyToMsg.isTopicMainMessage) {
newMsg.reply_to.forum_topic = true;
newMsg.reply_to.flags |= 8;
}
}
}
if (linkedToGroup != 0) {
@ -3690,7 +3758,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
newMsgObj.sendAnimationData = sendAnimationData;
newMsgObj.wasJustSent = true;
newMsgObj.scheduled = scheduleDate != 0;
if (!newMsgObj.isForwarded() && (newMsgObj.type == 3 || videoEditedInfo != null || newMsgObj.type == MessageObject.TYPE_VOICE) && !TextUtils.isEmpty(newMsg.attachPath)) {
if (!newMsgObj.isForwarded() && (newMsgObj.type == MessageObject.TYPE_VIDEO || videoEditedInfo != null || newMsgObj.type == MessageObject.TYPE_VOICE) && !TextUtils.isEmpty(newMsg.attachPath)) {
newMsgObj.attachPathExists = true;
}
if (newMsgObj.videoEditedInfo != null && videoEditedInfo == null) {
@ -3702,7 +3770,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
objArr.add(newMsgObj);
ArrayList<TLRPC.Message> arr = new ArrayList<>();
arr.add(newMsg);
MessagesStorage.getInstance(currentAccount).putMessages(arr, false, true, false, 0, scheduleDate != 0);
MessagesStorage.getInstance(currentAccount).putMessages(arr, false, true, false, 0, scheduleDate != 0, 0);
MessagesController.getInstance(currentAccount).updateInterfaceWithMessages(peer, objArr, scheduleDate != 0);
if (scheduleDate == 0) {
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.dialogsNeedReload);
@ -3744,6 +3812,10 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
reqSend.silent = newMsg.silent;
reqSend.peer = sendToPeer;
reqSend.random_id = newMsg.random_id;
if (replyToTopMsg != null) {
reqSend.top_msg_id = replyToTopMsg.getId();
reqSend.flags |= 512;
}
if (updateStickersOreder) {
reqSend.update_stickersets_order = true;
}
@ -4072,6 +4144,10 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
request = new TLRPC.TL_messages_sendMultiMedia();
request.peer = sendToPeer;
request.silent = newMsg.silent;
if (replyToTopMsg != null) {
request.top_msg_id = replyToTopMsg.getId();
request.flags |= 512;
}
if (newMsg.reply_to != null && newMsg.reply_to.reply_to_msg_id != 0) {
request.flags |= 1;
request.reply_to_msg_id = newMsg.reply_to.reply_to_msg_id;
@ -4104,6 +4180,10 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
TLRPC.TL_messages_sendMedia request = new TLRPC.TL_messages_sendMedia();
request.peer = sendToPeer;
request.silent = newMsg.silent;
if (replyToTopMsg != null) {
request.top_msg_id = replyToTopMsg.getId();
request.flags |= 512;
}
if (newMsg.reply_to != null && newMsg.reply_to.reply_to_msg_id != 0) {
request.flags |= 1;
request.reply_to_msg_id = newMsg.reply_to.reply_to_msg_id;
@ -4463,6 +4543,10 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
TLRPC.TL_messages_forwardMessages reqSend = new TLRPC.TL_messages_forwardMessages();
reqSend.to_peer = sendToPeer;
reqSend.with_my_score = retryMessageObject.messageOwner.with_my_score;
if (replyToTopMsg != null) {
reqSend.top_msg_id = replyToTopMsg.getId();
reqSend.flags |= 512;
}
if (params != null && params.containsKey("fwd_id")) {
int fwdId = Utilities.parseInt(params.get("fwd_id"));
reqSend.drop_author = true;
@ -4503,6 +4587,10 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
TLRPC.TL_messages_sendInlineBotResult reqSend = new TLRPC.TL_messages_sendInlineBotResult();
reqSend.peer = sendToPeer;
reqSend.random_id = newMsg.random_id;
if (replyToTopMsg != null) {
reqSend.top_msg_id = replyToTopMsg.getId();
reqSend.flags |= 512;
}
if (newMsg.from_id != null) {
reqSend.send_as = getMessagesController().getInputPeer(newMsg.from_id);
}
@ -4943,7 +5031,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
return;
} else if (add) {
delayedMessages.remove(key);
getMessagesStorage().putMessages(message.messages, false, true, false, 0, message.scheduled);
getMessagesStorage().putMessages(message.messages, false, true, false, 0, message.scheduled, 0);
getMessagesController().updateInterfaceWithMessages(message.peer, message.messageObjects, message.scheduled);
if (!message.scheduled) {
getNotificationCenter().postNotificationName(NotificationCenter.dialogsNeedReload);
@ -5279,7 +5367,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
getNotificationCenter().postNotificationName(NotificationCenter.messageReceivedByServer, oldId, newMsgObj.id, newMsgObj, newMsgObj.dialog_id, grouped_id, existFlags, scheduled);
getMessagesStorage().getStorageQueue().postRunnable(() -> {
getMessagesStorage().updateMessageStateAndId(newMsgObj.random_id, MessageObject.getPeerId(newMsgObj.peer_id), oldId, newMsgObj.id, 0, false, scheduled ? 1 : 0);
getMessagesStorage().putMessages(sentMessages, true, false, false, 0, scheduled);
getMessagesStorage().putMessages(sentMessages, true, false, false, 0, scheduled, 0);
AndroidUtilities.runOnUIThread(() -> {
getMediaDataController().increasePeerRaiting(newMsgObj.dialog_id);
getNotificationCenter().postNotificationName(NotificationCenter.messageReceivedByServer, oldId, newMsgObj.id, newMsgObj, newMsgObj.dialog_id, grouped_id, existFlags, scheduled);
@ -5573,7 +5661,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
messageIds.add(oldId);
getMessagesController().deleteMessages(messageIds, null, null, newMsgObj.dialog_id, false, true);
getMessagesStorage().getStorageQueue().postRunnable(() -> {
getMessagesStorage().putMessages(sentMessages, true, false, false, 0, false);
getMessagesStorage().putMessages(sentMessages, true, false, false, 0, false, 0);
AndroidUtilities.runOnUIThread(() -> {
ArrayList<MessageObject> messageObjects = new ArrayList<>();
messageObjects.add(new MessageObject(msgObj.currentAccount, msgObj.messageOwner, true, true));
@ -5590,7 +5678,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
getNotificationCenter().postNotificationName(NotificationCenter.messageReceivedByServer, oldId, newMsgObj.id, newMsgObj, newMsgObj.dialog_id, 0L, existFlags, scheduled);
getMessagesStorage().getStorageQueue().postRunnable(() -> {
getMessagesStorage().updateMessageStateAndId(newMsgObj.random_id, MessageObject.getPeerId(newMsgObj.peer_id), oldId, newMsgObj.id, 0, false, scheduled ? 1 : 0);
getMessagesStorage().putMessages(sentMessages, true, false, false, 0, scheduled);
getMessagesStorage().putMessages(sentMessages, true, false, false, 0, scheduled, 0);
AndroidUtilities.runOnUIThread(() -> {
getMediaDataController().increasePeerRaiting(newMsgObj.dialog_id);
getNotificationCenter().postNotificationName(NotificationCenter.messageReceivedByServer, oldId, newMsgObj.id, newMsgObj, newMsgObj.dialog_id, 0L, existFlags, scheduled);
@ -6525,7 +6613,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
TLRPC.TL_messages_messages messagesRes = new TLRPC.TL_messages_messages();
messagesRes.messages.add(prevMessage.messageOwner);
accountInstance.getMessagesStorage().putMessages(messagesRes, message.peer, -2, 0, false, scheduleDate != 0);
accountInstance.getMessagesStorage().putMessages(messagesRes, message.peer, -2, 0, false, scheduleDate != 0, 0);
instance.sendReadyToSendGroup(message, true, true);
}
});
@ -7482,11 +7570,13 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
if (thumb != null) {
int side = isEncrypted || info.ttl != 0 ? 90 : Math.max(thumb.getWidth(), thumb.getHeight());
size = ImageLoader.scaleAndSaveImage(thumb, side, side, side > 90 ? 80 : 55, isEncrypted);
thumbKey = getKeyForPhotoSize(accountInstance, size, null, true, false);
if (size != null && size.location != null) {
thumbKey = getKeyForPhotoSize(accountInstance, size, null, true, false);
String fileName = size.location.volume_id + "_" + size.location.local_id + ".jpg";
File fileDir = FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE);
localPath = new File(fileDir, fileName).getAbsolutePath();
String fileName = size.location.volume_id + "_" + size.location.local_id + ".jpg";
File fileDir = FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE);
localPath = new File(fileDir, fileName).getAbsolutePath();
}
}
document = new TLRPC.TL_document();
document.file_reference = new byte[0];

View File

@ -97,6 +97,7 @@ public class SharedConfig {
public static boolean stickersReorderingHintUsed;
public static boolean disableVoiceAudioEffects;
public static boolean forceDisableTabletMode;
public static boolean useLNavigation;
private static int lastLocalId = -210000;
public static String storageCacheDir;
@ -168,6 +169,8 @@ public class SharedConfig {
public static int fastScrollHintCount = 3;
public static boolean dontAskManageStorage;
public static boolean isFloatingDebugActive;
static {
loadConfig();
}
@ -243,7 +246,6 @@ public class SharedConfig {
editor.putBoolean("forwardingOptionsHintShown", forwardingOptionsHintShown);
editor.putInt("lockRecordAudioVideoHint", lockRecordAudioVideoHint);
editor.putString("storageCacheDir", !TextUtils.isEmpty(storageCacheDir) ? storageCacheDir : "");
editor.putBoolean("hasEmailLogin", hasEmailLogin);
if (pendingAppUpdate != null) {
try {
@ -262,6 +264,12 @@ public class SharedConfig {
editor.putLong("appUpdateCheckTime", lastUpdateCheckTime);
editor.apply();
editor = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Context.MODE_PRIVATE).edit();
editor.putBoolean("hasEmailLogin", hasEmailLogin);
editor.putBoolean("useLNavigation", useLNavigation);
editor.putBoolean("floatingDebugActive", isFloatingDebugActive);
editor.apply();
} catch (Exception e) {
FileLog.e(e);
}
@ -426,6 +434,8 @@ public class SharedConfig {
fastScrollHintCount = preferences.getInt("fastScrollHintCount", 3);
dontAskManageStorage = preferences.getBoolean("dontAskManageStorage", false);
hasEmailLogin = preferences.getBoolean("hasEmailLogin", false);
useLNavigation = preferences.getBoolean("useLNavigation", BuildVars.DEBUG_VERSION);
isFloatingDebugActive = preferences.getBoolean("floatingDebugActive", false);
preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE);
showNotificationsForAllAccounts = preferences.getBoolean("AllAccounts", true);
@ -444,7 +454,7 @@ public class SharedConfig {
public static void updateTabletConfig() {
if (fontSizeIsDefault) {
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("userconfing", Context.MODE_PRIVATE);
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
fontSize = preferences.getInt("fons_size", AndroidUtilities.isTablet() ? 18 : 16);
ivFontSize = preferences.getInt("iv_font_size", fontSize);
}

View File

@ -39,6 +39,7 @@ import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.SparseArray;
import org.telegram.ui.ActionBar.Theme;
import org.xml.sax.Attributes;
@ -122,6 +123,7 @@ public class SvgHelper {
private Theme.ResourcesProvider currentResourcesProvider;
private float colorAlpha;
private float crossfadeAlpha = 1.0f;
SparseArray<Paint> overridePaintByPosition = new SparseArray<>();
private boolean aspectFill = true;
@ -225,11 +227,15 @@ public class SvgHelper {
Object object = commands.get(a);
if (object instanceof Matrix) {
canvas.save();
// canvas.concat((Matrix) object);
canvas.concat((Matrix) object);
} else if (object == null) {
canvas.restore();
} else {
Paint paint;
Paint overridePaint = overridePaintByPosition.get(a);
if (overridePaint == null) {
overridePaint = this.overridePaint;
}
if (drawInBackground) {
paint = backgroundPaint;
} else if (overridePaint != null) {
@ -352,6 +358,14 @@ public class SvgHelper {
public void setPaint(Paint paint) {
overridePaint = paint;
}
public void setPaint(Paint paint, int position) {
overridePaintByPosition.put(position, paint);
}
public void copyCommandFromPosition(int position) {
commands.add(commands.get(position));
}
}
public static Bitmap getBitmap(int res, int width, int height, int color) {

View File

@ -0,0 +1,886 @@
package org.telegram.messenger;
import android.content.SharedPreferences;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.SparseArray;
import androidx.collection.LongSparseArray;
import org.telegram.SQLite.SQLiteCursor;
import org.telegram.messenger.support.LongSparseIntArray;
import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.NativeByteBuffer;
import org.telegram.tgnet.RequestDelegate;
import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.Components.Forum.ForumUtilities;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
public class TopicsController extends BaseController {
public static final int TOPIC_FLAG_TITLE = 1;
public static final int TOPIC_FLAG_ICON = 2;
public static final int TOPIC_FLAG_PIN = 4;
public static final int TOPIC_FLAG_CLOSE = 8;
private static final int MAX_PRELOAD_COUNT = 20;
public static final int LOAD_TYPE_PRELOAD = 0;
public static final int LOAD_TYPE_LOAD_NEXT = 1;
public static final int LOAD_TYPE_LOAD_UNKNOWN = 2;
LongSparseArray<ArrayList<TLRPC.TL_forumTopic>> topicsByChatId = new LongSparseArray<>();
LongSparseArray<LongSparseArray<TLRPC.TL_forumTopic>> topicsMapByChatId = new LongSparseArray<>();
LongSparseIntArray topicsIsLoading = new LongSparseIntArray();
LongSparseIntArray endIsReached = new LongSparseIntArray();
LongSparseArray<TLRPC.TL_forumTopic> topicsByTopMsgId = new LongSparseArray<>();
LongSparseIntArray currentOpenTopicsCounter = new LongSparseIntArray();
LongSparseIntArray openedTopicsBuChatId = new LongSparseIntArray();
public TopicsController(int num) {
super(num);
}
public void preloadTopics(long chatId) {
loadTopics(chatId, true, LOAD_TYPE_PRELOAD);
}
public void loadTopics(long chatId) {
loadTopics(chatId, false, LOAD_TYPE_LOAD_NEXT);
}
private void loadTopics(long chatId, boolean fromCache, int loadType) {
if (topicsIsLoading.get(chatId, 0) != 0) {
return;
}
if (BuildVars.DEBUG_PRIVATE_VERSION) {
FileLog.d("load topics " + chatId + " fromCache=" + fromCache + " loadType=" + loadType);
}
if (fromCache) {
getMessagesStorage().loadTopics(-chatId, topics -> {
AndroidUtilities.runOnUIThread(() -> {
if (BuildVars.DEBUG_PRIVATE_VERSION) {
FileLog.d("loaded from cache " + chatId + " topics_count=" + (topics == null ? 0 : topics.size()));
}
topicsIsLoading.put(chatId, 0);
processTopics(chatId, topics, null, fromCache, loadType, -1);
});
});
return;
}
topicsIsLoading.put(chatId, 1);
TLRPC.TL_channels_getForumTopics getForumTopics = new TLRPC.TL_channels_getForumTopics();
getForumTopics.channel = getMessagesController().getInputChannel(chatId);
if (loadType == LOAD_TYPE_PRELOAD) {
getForumTopics.limit = MAX_PRELOAD_COUNT;
} else if (loadType == LOAD_TYPE_LOAD_NEXT) {
getForumTopics.limit = 100;
TopicsLoadOffset loadOffsets = getLoadOffset(chatId);
getForumTopics.offset_date = loadOffsets.lastMessageDate;
getForumTopics.offset_id = loadOffsets.lastMessageId;
getForumTopics.offset_topic = loadOffsets.lastTopicId;
if (BuildVars.DEBUG_PRIVATE_VERSION) {
FileLog.d("offset_date=" + loadOffsets.lastMessageDate + " offset_id=" + loadOffsets.lastMessageId + " offset_topic=" + loadOffsets.lastTopicId);
}
}
getConnectionsManager().sendRequest(getForumTopics, (response, error) -> {
if (response != null) {
SparseArray<TLRPC.Message> messagesMap = new SparseArray<>();
TLRPC.TL_messages_forumTopics topics = (TLRPC.TL_messages_forumTopics) response;
for (int i = 0; i < topics.messages.size(); i++) {
messagesMap.put(topics.messages.get(i).id, topics.messages.get(i));
}
AndroidUtilities.runOnUIThread(() -> {
getMessagesController().putUsers(((TLRPC.TL_messages_forumTopics) response).users, false);
getMessagesController().putChats(((TLRPC.TL_messages_forumTopics) response).chats, false);
topicsIsLoading.put(chatId, 0);
processTopics(chatId, topics.topics, messagesMap, false, loadType, ((TLRPC.TL_messages_forumTopics) response).count);
getMessagesStorage().putMessages(topics.messages, false, true, false, 0, false, 0);
getMessagesStorage().saveTopics(-chatId, topicsByChatId.get(chatId), true, true);
if (!topics.topics.isEmpty() && loadType == LOAD_TYPE_LOAD_NEXT) {
TLRPC.TL_forumTopic lastTopic = topics.topics.get(topics.topics.size() - 1);
TLRPC.Message lastTopicMessage = messagesMap.get(lastTopic.top_message);
saveLoadOffset(chatId, lastTopic.top_message, lastTopicMessage.date, lastTopic.id);
} else if (getTopics(chatId) == null || getTopics(chatId).size() < topics.count) {
clearLoadingOffset(chatId);
loadTopics(chatId);
}
});
} else {
AndroidUtilities.runOnUIThread(() -> {
topicsIsLoading.put(chatId, 0);
getNotificationCenter().postNotificationName(NotificationCenter.topicsDidLoaded, chatId, false);
});
}
});
}
public void processTopics(long chatId, ArrayList<TLRPC.TL_forumTopic> newTopics, SparseArray<TLRPC.Message> messagesMap, boolean fromCache, int loadType, int totalCount) {
ArrayList<TLRPC.TL_forumTopic> topics = topicsByChatId.get(chatId);
ArrayList<TLRPC.TL_forumTopic> topicsToReload = null;
LongSparseArray<TLRPC.TL_forumTopic> topicsMap = topicsMapByChatId.get(chatId);
if (topics == null) {
topics = new ArrayList<>();
topicsByChatId.put(chatId, topics);
}
if (topicsMap == null) {
topicsMap = new LongSparseArray<>();
topicsMapByChatId.put(chatId, topicsMap);
}
boolean changed = false;
if (newTopics != null) {
for (int i = 0; i < newTopics.size(); i++) {
TLRPC.TL_forumTopic newTopic = newTopics.get(i);
if (!topicsMap.containsKey(newTopic.id)) {
if (messagesMap != null) {
newTopic.topMessage = messagesMap.get(newTopic.top_message);
newTopic.topicStartMessage = messagesMap.get(newTopic.id);
}
if (newTopic.topMessage == null) {
if (topicsToReload == null) {
topicsToReload = new ArrayList<>();
}
topicsToReload.add(newTopic);
} else {
if (newTopic.topicStartMessage == null) {
newTopic.topicStartMessage = new TLRPC.TL_message();
newTopic.topicStartMessage.message = "";
newTopic.topicStartMessage.id = newTopic.id;
newTopic.topicStartMessage.peer_id = getMessagesController().getPeer(-chatId);
newTopic.topicStartMessage.action = new TLRPC.TL_messageActionTopicCreate();
newTopic.topicStartMessage.action.title = newTopic.title;
}
topics.add(newTopic);
topicsMap.put(newTopic.id, newTopic);
topicsByTopMsgId.put(messageHash(newTopic.top_message, chatId), newTopic);
changed = true;
}
}
}
}
if (changed) {
sortTopics(chatId);
}
if (topicsToReload != null) {
reloadTopics(chatId, topicsToReload);
} else if (loadType == LOAD_TYPE_LOAD_NEXT && topics.size() >= totalCount && totalCount >= 0) {
endIsReached.put(chatId, 1);
}
getNotificationCenter().postNotificationName(NotificationCenter.topicsDidLoaded, chatId, true);
if ((loadType == LOAD_TYPE_PRELOAD || (loadType == LOAD_TYPE_PRELOAD && !fromCache)) && fromCache && topicsByChatId.get(chatId).isEmpty()) {
loadTopics(chatId, false, LOAD_TYPE_PRELOAD);
}
}
private void updateDialogUnreadCount(long chatId) {
int[] counters = getForumUnreadCount(chatId);
int unread = counters[0];
int unread_mentions = counters[1];
// getMessagesStorage().getStorageQueue().postRunnable(() -> {
// SQLiteDatabase database = getMessagesStorage().getDatabase();
// try {
// SQLiteCursor cursor = database.queryFinalized("SELECT unread_count, unread_count_i FROM dialogs WHERE did = " + (-chatId));
// boolean needUpdate = false;
// if (cursor.next()) {
// int currentUnreadCount = cursor.intValue(0);
// int currentUnreadMentions = cursor.intValue(1);
// if ((unread > 0) != (currentUnreadCount > 0) || (unread_mentions > 0) != (currentUnreadMentions > 0)) {
// needUpdate = true;
// }
// Log.d("kek", "update dialog read by topics " + chatId + " " + unread + " " + unread_mentions + " current " + currentUnreadCount + " " + currentUnreadMentions + " update " + needUpdate);
// }
// cursor.dispose();
// if (needUpdate) {
// database.executeFast(String.format(Locale.US, "UPDATE dialogs SET unread_count = %d, unread_count_i - %d WHERE did = %d", unread, unread_mentions, -chatId)).stepThis().dispose();
// SQLiteCursor cursor2 = database.queryFinalized("SELECT unread_count, unread_count_i FROM dialogs WHERE did = " + (-chatId));
// if (cursor2.next()) {
// int currentUnreadCount = cursor2.intValue(0);
// Log.d("kek", "currentUnreadCount " + currentUnreadCount);
// }
// cursor2.dispose();
// getMessagesStorage().resetAllUnreadCounters(false);
// }
// } catch (Throwable e) {
// e.printStackTrace();
// }
// });
}
private long messageHash(int messageId, long chatId) {
return chatId + ((long) messageId << 12);
}
public ArrayList<TLRPC.TL_forumTopic> getTopics(long chatId) {
return topicsByChatId.get(chatId);
}
private void sortTopics(long chatId) {
sortTopics(chatId, true);
}
private void sortTopics(long chatId, boolean notify) {
ArrayList<TLRPC.TL_forumTopic> topics = topicsByChatId.get(chatId);
if (topics != null) {
if (openedTopicsBuChatId.get(chatId, 0) > 0) {
Collections.sort(topics, Comparator.comparingInt(o -> o.pinned ? -1 : -o.topMessage.date));
}
if (notify) {
getNotificationCenter().postNotificationName(NotificationCenter.topicsDidLoaded, chatId, true);
}
}
}
public void updateTopicsWithDeletedMessages(long dialogId, ArrayList<Integer> messages) {
if (dialogId > 0) {
return;
}
long chatId = -dialogId;
AndroidUtilities.runOnUIThread(() -> {
getMessagesStorage().getStorageQueue().postRunnable(() -> {
ArrayList<TLRPC.TL_forumTopic> topicsToUpdate = null;
try {
SQLiteCursor cursor = getMessagesStorage().getDatabase().queryFinalized(String.format(Locale.US, "SELECT topic_id, top_message FROM topics WHERE did = %d AND top_message IN (%s)", dialogId, TextUtils.join(",", messages)));
while (cursor.next()) {
if (topicsToUpdate == null) {
topicsToUpdate = new ArrayList<>();
}
TLRPC.TL_forumTopic topic = new TLRPC.TL_forumTopic();
topic.id = cursor.intValue(0);
topic.top_message = cursor.intValue(1);
topic.from_id = getMessagesController().getPeer(getUserConfig().clientUserId);
topic.notify_settings = new TLRPC.TL_peerNotifySettings();
topicsToUpdate.add(topic);
}
cursor.dispose();
if (topicsToUpdate != null) {
for (int i = 0; i < topicsToUpdate.size(); i++) {
TLRPC.TL_forumTopic topic = topicsToUpdate.get(i);
cursor = getMessagesStorage().getDatabase().queryFinalized(String.format(Locale.US, "SELECT mid, data FROM messages_topics WHERE uid = %d AND topic_id = %d ORDER BY mid DESC LIMIT 1", dialogId, topic.id));
if (cursor.next()) {
NativeByteBuffer data = cursor.byteBufferValue(1);
if (data != null) {
TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false);
message.readAttachPath(data, getUserConfig().clientUserId);
data.reuse();
topicsByTopMsgId.remove(messageHash(topic.top_message, chatId));
topic.top_message = message.id;
topic.topMessage = message;
topic.groupedMessages = null;
topicsByTopMsgId.put(messageHash(topic.top_message, chatId), topic);
}
}
cursor.dispose();
}
for (int i = 0; i < topicsToUpdate.size(); i++) {
getMessagesStorage().getDatabase().executeFast(String.format(Locale.US, "UPDATE topics SET top_message = %d WHERE did = %d AND topic_id = %d", topicsToUpdate.get(i).top_message, dialogId, topicsToUpdate.get(i).id)).stepThis().dispose();
}
}
} catch (Exception e) {
e.printStackTrace();
}
ArrayList<TLRPC.TL_forumTopic> finalTopicsToUpdate = topicsToUpdate;
getMessagesStorage().loadGroupedMessagesForTopics(dialogId, finalTopicsToUpdate);
if (finalTopicsToUpdate != null) {
AndroidUtilities.runOnUIThread(() -> {
boolean changed = false;
ArrayList<TLRPC.TL_forumTopic> topicsToReload = null;
for (int i = 0; i < finalTopicsToUpdate.size(); i++) {
TLRPC.TL_forumTopic fromUpdate = finalTopicsToUpdate.get(i);
LongSparseArray<TLRPC.TL_forumTopic> map = topicsMapByChatId.get(chatId);
TLRPC.TL_forumTopic toUpdate;
if (map != null) {
toUpdate = map.get(fromUpdate.id);
if (toUpdate != null && fromUpdate.top_message != -1 && fromUpdate.topMessage != null) {
topicsByTopMsgId.remove(messageHash(toUpdate.top_message, chatId));
toUpdate.top_message = fromUpdate.topMessage.id;
toUpdate.topMessage = fromUpdate.topMessage;
toUpdate.groupedMessages = fromUpdate.groupedMessages;
topicsByTopMsgId.put(messageHash(toUpdate.top_message, chatId), toUpdate);
changed = true;
} else if (fromUpdate.top_message == -1 || fromUpdate.topMessage == null) {
if (topicsToReload == null) {
topicsToReload = new ArrayList<>();
}
topicsToReload.add(fromUpdate);
}
}
}
if (changed) {
sortTopics(chatId);
}
if (topicsToReload != null) {
reloadTopics(chatId, topicsToReload);
}
});
}
});
});
}
private void reloadTopics(long chatId, ArrayList<TLRPC.TL_forumTopic> topicsToReload) {
TLRPC.TL_channels_getForumTopicsByID req = new TLRPC.TL_channels_getForumTopicsByID();
for (int i = 0; i < topicsToReload.size(); i++) {
req.topics.add(topicsToReload.get(i).id);
}
req.channel = getMessagesController().getInputChannel(chatId);
getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> {
if (response != null) {
SparseArray<TLRPC.Message> messagesMap = new SparseArray<>();
TLRPC.TL_messages_forumTopics topics = (TLRPC.TL_messages_forumTopics) response;
for (int i = 0; i < topics.messages.size(); i++) {
messagesMap.put(topics.messages.get(i).id, topics.messages.get(i));
}
AndroidUtilities.runOnUIThread(() -> {
getMessagesController().putUsers(((TLRPC.TL_messages_forumTopics) response).users, false);
getMessagesController().putChats(((TLRPC.TL_messages_forumTopics) response).chats, false);
processTopics(chatId, topics.topics, messagesMap, false, LOAD_TYPE_LOAD_UNKNOWN, -1);
getMessagesStorage().putMessages(topics.messages, false, true, false, 0, false, 0);
getMessagesStorage().saveTopics(-chatId, topicsByChatId.get(chatId), true, true);
});
}
}));
}
public void updateMaxReadId(long chatId, int topicId, int readMaxId, int unreadCount) {
TLRPC.TL_forumTopic topic = findTopic(chatId, topicId);
if (topic != null) {
topic.read_inbox_max_id = readMaxId;
topic.unread_count = unreadCount;
sortTopics(chatId);
}
}
public TLRPC.TL_forumTopic findTopic(long chatId, int topicId) {
LongSparseArray<TLRPC.TL_forumTopic> topicsMap = topicsMapByChatId.get(chatId);
if (topicsMap != null) {
return topicsMap.get(topicId);
}
return null;
}
public String getTopicName(TLRPC.Chat chat, MessageObject message) {
if (message.messageOwner.reply_to == null) {
return null;
}
int topicId = message.messageOwner.reply_to.reply_to_top_id;
if (topicId == 0) {
topicId = message.messageOwner.reply_to.reply_to_msg_id;
}
if (topicId != 0) {
TLRPC.TL_forumTopic topic = findTopic(chat.id, topicId);
if (topic != null) {
return topic.title;
}
}
return "";
}
public CharSequence getTopicIconName(TLRPC.Chat chat, MessageObject message, TextPaint paint) {
if (message.messageOwner.reply_to == null) {
return null;
}
int topicId = message.messageOwner.reply_to.reply_to_top_id;
if (topicId == 0) {
topicId = message.messageOwner.reply_to.reply_to_msg_id;
}
if (topicId != 0) {
TLRPC.TL_forumTopic topic = findTopic(chat.id, topicId);
if (topic != null) {
return ForumUtilities.getTopicSpannedName(topic, paint);
}
}
return null;
}
private final static int[] countsTmp = new int[4];
public int[] getForumUnreadCount(long chatId) {
ArrayList<TLRPC.TL_forumTopic> topics = topicsByChatId.get(chatId);
Arrays.fill(countsTmp, 0);
if (topics != null) {
for (int i = 0; i < topics.size(); i++) {
TLRPC.TL_forumTopic topic = topics.get(i);
countsTmp[0] += topic.unread_count > 0 ? 1 : 0;
countsTmp[1] += topic.unread_mentions_count > 0 ? 1 : 0;
countsTmp[2] += topic.unread_reactions_count > 0 ? 1 : 0;
if (!getMessagesController().isDialogMuted(-chatId, topic.id)) {
countsTmp[3] += topic.unread_count;
}
}
}
return countsTmp;
}
public void onTopicCreated(long dialogId, TLRPC.TL_forumTopic forumTopic, boolean saveInDatabase) {
LongSparseArray<TLRPC.TL_forumTopic> map = topicsMapByChatId.get(-dialogId);
if (findTopic(-dialogId, forumTopic.id) != null) {
return;
}
if (map == null) {
map = new LongSparseArray<>();
topicsMapByChatId.put(-dialogId, map);
}
ArrayList<TLRPC.TL_forumTopic> list = topicsByChatId.get(-dialogId);
if (list == null) {
list = new ArrayList<>();
topicsByChatId.put(-dialogId, list);
}
map.put(forumTopic.id, forumTopic);
list.add(forumTopic);
if (saveInDatabase) {
getMessagesStorage().saveTopics(dialogId, Collections.singletonList(forumTopic), false, true);
}
sortTopics(-dialogId, true);
}
public void onTopicEdited(long dialogId, TLRPC.TL_forumTopic forumTopic) {
getMessagesStorage().updateTopicData(dialogId, forumTopic, TOPIC_FLAG_ICON + TOPIC_FLAG_TITLE);
sortTopics(-dialogId);
}
public void deleteTopics(long chatId, ArrayList<Integer> topicIds) {
ArrayList<TLRPC.TL_forumTopic> topicsArray = topicsByChatId.get(chatId);
LongSparseArray<TLRPC.TL_forumTopic> topicsMap = topicsMapByChatId.get(chatId);
if (topicsMap != null && topicsArray != null) {
for (int i = 0; i < topicIds.size(); i++) {
int topicId = topicIds.get(i);
TLRPC.TL_forumTopic topic = topicsMap.get(topicId);
topicsMap.remove(topicId);
if (topic != null) {
topicsByTopMsgId.remove(messageHash(topic.top_message, chatId));
topicsArray.remove(topic);
}
}
sortTopics(chatId);
}
for (int i = 0; i < topicIds.size(); i++) {
deleteTopic(chatId, topicIds.get(i), 0);
}
}
private void deleteTopic(long chatId, int topicId, int offset) {
TLRPC.TL_channels_deleteTopicHistory deleteTopicHistory = new TLRPC.TL_channels_deleteTopicHistory();
deleteTopicHistory.channel = getMessagesController().getInputChannel(chatId);
deleteTopicHistory.top_msg_id = topicId;
if (offset == 0) {
getMessagesStorage().removeTopic(-chatId, topicId);
}
ConnectionsManager.getInstance(currentAccount).sendRequest(deleteTopicHistory, new RequestDelegate() {
@Override
public void run(TLObject response, TLRPC.TL_error error) {
if (error == null) {
TLRPC.TL_messages_affectedHistory res = (TLRPC.TL_messages_affectedHistory) response;
getMessagesController().processNewChannelDifferenceParams(res.pts, res.pts_count, chatId);
if (res.offset > 0) {
deleteTopic(chatId, topicId, res.offset);
}
}
}
});
}
public void toggleCloseTopic(long chatId, int topicId, boolean close) {
TLRPC.TL_channels_editForumTopic req = new TLRPC.TL_channels_editForumTopic();
req.channel = getMessagesController().getInputChannel(chatId);
req.topic_id = topicId;
req.flags |= 4;
req.closed = close;
LongSparseArray<TLRPC.TL_forumTopic> topicsMap = topicsMapByChatId.get(chatId);
if (topicsMap != null) {
TLRPC.TL_forumTopic topic = topicsMap.get(topicId);
if (topic != null) {
topic.closed = close;
getMessagesStorage().updateTopicData(-chatId, topic, TOPIC_FLAG_CLOSE);
}
}
ConnectionsManager.getInstance(currentAccount).sendRequest(req, new RequestDelegate() {
@Override
public void run(TLObject response, TLRPC.TL_error error) {
if (error == null) {
}
}
});
}
public void pinTopic(long chatId, int topicId, boolean pin) {
TLRPC.TL_channels_updatePinnedForumTopic req = new TLRPC.TL_channels_updatePinnedForumTopic();
req.channel = getMessagesController().getInputChannel(chatId);
req.topic_id = topicId;
req.pinned = pin;
ArrayList<TLRPC.TL_forumTopic> topics = topicsByChatId.get(chatId);
if (topics != null) {
for (int i = 0; i < topics.size(); ++i) {
TLRPC.TL_forumTopic topic = topics.get(i);
if (topic != null && (topicId == topic.id && pin) != topic.pinned) {
topic.pinned = topicId == topic.id && pin;
// topic.flags = topic.pinned ? (topic.flags | 8) : (topic.flags &~ 8);
getMessagesStorage().updateTopicData(-chatId, topic, TOPIC_FLAG_PIN);
}
}
}
sortTopics(chatId);
ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> {
if (response instanceof TLRPC.Updates) {
// getMessagesController().processUpdates((TLRPC.Updates) response, false);
}
});
}
public void updateMentionsUnread(long dialogId, int topicId, int topicMentionsCount) {
AndroidUtilities.runOnUIThread(() -> {
TLRPC.TL_forumTopic topic = findTopic(-dialogId, topicId);
if (topic != null) {
topic.unread_mentions_count = topicMentionsCount;
sortTopics(-dialogId, true);
}
});
}
public int updateReactionsUnread(long dialogId, int topicId, int count, boolean increment) {
TLRPC.TL_forumTopic topic = findTopic(-dialogId, topicId);
int totalCount = -1;
if (topic != null) {
if (increment) {
topic.unread_reactions_count += count;
if (topic.unread_reactions_count < 0) {
topic.unread_reactions_count = 0;
}
} else {
topic.unread_reactions_count = count;
}
totalCount = topic.unread_reactions_count;
sortTopics(-dialogId, true);
}
return totalCount;
}
public void markAllReactionsAsRead(long chatId, int topicId) {
TLRPC.TL_forumTopic topic = findTopic(chatId, topicId);
if (topic != null && topic.unread_reactions_count > 0) {
topic.unread_reactions_count = 0;
sortTopics(chatId);
}
}
public TLRPC.Message getLastMessage(long id) {
ArrayList<TLRPC.TL_forumTopic> topics = topicsByChatId.get(id);
if (!topics.isEmpty()) {
return topics.get(0).topMessage;
}
return null;
}
LongSparseArray<TopicsLoadOffset> offsets = new LongSparseArray<>();
public TopicsLoadOffset getLoadOffset(long chatId) {
TopicsLoadOffset offset = offsets.get(chatId);
if (offset != null) {
return offset;
}
return new TopicsLoadOffset();
// SharedPreferences sharedPreferences = getUserConfig().getPreferences();
// TopicsLoadOffset topicsLoadOffset = new TopicsLoadOffset();
// topicsLoadOffset.lastMessageId = sharedPreferences.getInt("topics_load_offset_message_id_" + chatId, 0);
// topicsLoadOffset.lastMessageDate = sharedPreferences.getInt("topics_load_offset_date_" + chatId, 0);
// topicsLoadOffset.lastMessageId = sharedPreferences.getInt("topics_load_offset_topic_id_" + chatId, 0);
// return topicsLoadOffset;
}
public void saveLoadOffset(long chatId, int lastMessageId, int lastMessageDate, int lastTopicId) {
TopicsLoadOffset offset = new TopicsLoadOffset();
offset.lastMessageId = lastMessageId;
offset.lastMessageDate = lastMessageDate;
offset.lastTopicId = lastTopicId;
offsets.put(chatId, offset);
// SharedPreferences.Editor editor = getUserConfig().getPreferences().edit();
// editor.putInt("topics_load_offset_message_id_" + chatId, lastMessageId);
// editor.putInt("topics_load_offset_date_" + chatId, lastMessageDate);
// editor.putInt("topics_load_offset_topic_id_" + chatId, lastTopicId);
// editor.apply();
}
public void clearLoadingOffset(long chatId) {
offsets.remove(chatId);
// SharedPreferences.Editor editor = getUserConfig().getPreferences().edit();
// editor.remove("topics_load_offset_message_id_" + chatId);
// editor.remove("topics_load_offset_date_" + chatId);
// editor.remove("topics_load_offset_topic_id_" + chatId);
// editor.apply();
}
public boolean endIsReached(long chatId) {
return endIsReached.get(chatId, 0) == 1;
}
public void processUpdate(List<TopicUpdate> topicUpdates) {
AndroidUtilities.runOnUIThread(() -> {
HashSet<Long> changedDialogs = new HashSet<>();
LongSparseArray<ArrayList<TLRPC.TL_forumTopic>> topicsToReload = null;
for (int i = 0; i < topicUpdates.size(); i++) {
TopicUpdate update = topicUpdates.get(i);
if (update.reloadTopic) {
if (topicsToReload == null) {
topicsToReload = new LongSparseArray<>();
}
ArrayList<TLRPC.TL_forumTopic> arrayList = topicsToReload.get(update.dialogId);
if (arrayList == null) {
arrayList = new ArrayList<>();
topicsToReload.put(update.dialogId, arrayList);
}
TLRPC.TL_forumTopic forumTopic = new TLRPC.TL_forumTopic();
forumTopic.id = update.topicId;
arrayList.add(forumTopic);
} else {
TLRPC.TL_forumTopic topic = findTopic(-update.dialogId, update.topicId);
if (topic != null) {
if (update.onlyCounters) {
if (update.unreadCount >= 0) {
topic.unread_count = update.unreadCount;
}
if (update.unreadMentions >= 0) {
topic.unread_mentions_count = update.unreadMentions;
}
} else {
topicsByTopMsgId.remove(messageHash(topic.top_message, -update.dialogId));
topic.topMessage = update.topMessage;
topic.groupedMessages = update.groupedMessages;
topic.top_message = update.topMessageId;
topic.unread_count = update.unreadCount;
topic.unread_mentions_count = update.unreadMentions;
topicsByTopMsgId.put(messageHash(topic.top_message, -update.dialogId), topic);
}
changedDialogs.add(-update.dialogId);
}
}
}
for (Long changedDialog : changedDialogs) {
sortTopics(changedDialog, true);
}
if (topicsToReload != null) {
for (int i = 0; i < topicsToReload.size(); i++) {
long dialogId = topicsToReload.keyAt(i);
ArrayList<TLRPC.TL_forumTopic> topics = topicsToReload.valueAt(i);
reloadTopics(-dialogId, topics);
}
}
});
}
public boolean isLoading(long chatId) {
return topicsIsLoading.get(chatId, 0) == 1 && (topicsByChatId.get(chatId) == null || topicsByChatId.get(chatId).isEmpty());
}
public void onTopicsDeletedServerSide(ArrayList<MessagesStorage.TopicKey> topicsToDelete) {
AndroidUtilities.runOnUIThread(() -> {
HashSet<Long> changedChatId = new HashSet<>();
for (int i = 0; i < topicsToDelete.size(); i++) {
MessagesStorage.TopicKey topicKey = topicsToDelete.get(i);
long chatId = -topicKey.dialogId;
LongSparseArray<TLRPC.TL_forumTopic> topicsMap = topicsMapByChatId.get(chatId);
if (topicsMap != null) {
topicsMap.remove(topicKey.topicId);
}
ArrayList<TLRPC.TL_forumTopic> topics = topicsByChatId.get(chatId);
if (topics != null) {
for (int k = 0; k < topics.size(); k++) {
if (topics.get(k).id == topicKey.topicId) {
topics.remove(k);
getNotificationCenter().postNotificationName(NotificationCenter.dialogDeleted, -chatId, topicKey.topicId);
changedChatId.add(chatId);
break;
}
}
}
}
for (Long chatId : changedChatId) {
sortTopics(chatId, true);
}
});
}
public void reloadTopics(long chatId) {
AndroidUtilities.runOnUIThread(() -> {
topicsByChatId.remove(chatId);
topicsMapByChatId.remove(chatId);
endIsReached.delete(chatId);
clearLoadingOffset(chatId);
TLRPC.Chat chat = getMessagesController().getChat(chatId);
if (chat != null && chat.forum) {
preloadTopics(chatId);
}
});
}
public void databaseCleared() {
AndroidUtilities.runOnUIThread(() -> {
topicsByChatId.clear();
topicsMapByChatId.clear();
endIsReached.clear();
SharedPreferences.Editor editor = getUserConfig().getPreferences().edit();
for (String key : getUserConfig().getPreferences().getAll().keySet()) {
if (key.startsWith("topics_load_offset_message_id_")) {
editor.remove(key);
}
if (key.startsWith("topics_load_offset_date_")) {
editor.remove(key);
}
if (key.startsWith("topics_load_offset_topic_id_")) {
editor.remove(key);
}
}
editor.apply();
});
}
public void updateReadOutbox(HashMap<MessagesStorage.TopicKey, Integer> topicsReadOutbox) {
AndroidUtilities.runOnUIThread(() -> {
HashSet<Long> updatedChats = new HashSet<>();
for (MessagesStorage.TopicKey topicKey : topicsReadOutbox.keySet()) {
int value = topicsReadOutbox.get(topicKey);
TLRPC.TL_forumTopic topic = findTopic(-topicKey.dialogId, topicKey.topicId);
if (topic != null) {
topic.read_outbox_max_id = Math.max(topic.read_outbox_max_id, value);
updatedChats.add(-topicKey.dialogId);
if ( topic.read_outbox_max_id >= topic.topMessage.id) {
topic.topMessage.unread = false;
}
}
}
//TODO topics
// optimize move to mask update
for (Long chatId : updatedChats) {
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.topicsDidLoaded, chatId, true);
}
});
}
public void updateTopicInUi(long dialogId, TLRPC.TL_forumTopic forumTopic, int flags) {
TLRPC.TL_forumTopic topic = findTopic(-dialogId, forumTopic.id);
if (topic != null) {
if ((flags & TOPIC_FLAG_TITLE) != 0) {
topic.title = forumTopic.title;
}
if ((flags & TOPIC_FLAG_ICON) != 0) {
topic.icon_emoji_id = forumTopic.icon_emoji_id;
}
if ((flags & TOPIC_FLAG_CLOSE) != 0) {
topic.closed = forumTopic.closed;
}
if ((flags & TOPIC_FLAG_PIN) != 0) {
topic.pinned = forumTopic.pinned;
}
sortTopics(-dialogId);
}
}
public void processEditedMessages(LongSparseArray<ArrayList<MessageObject>> editingMessagesFinal) {
HashSet<Long> changedChatId = new HashSet<>();
for (int i = 0; i < editingMessagesFinal.size(); i++) {
ArrayList<MessageObject> messageObjects = editingMessagesFinal.valueAt(i);
for (int j = 0; j < messageObjects.size(); j++) {
TLRPC.TL_forumTopic topic = topicsByTopMsgId.get(messageHash(messageObjects.get(j).getId(), -messageObjects.get(j).getDialogId()));
if (topic != null) {
topic.topMessage = messageObjects.get(j).messageOwner;
changedChatId.add(-messageObjects.get(j).getDialogId());
}
}
}
for (Long chatId : changedChatId) {
sortTopics(chatId, true);
}
}
public void processEditedMessage(TLRPC.Message newMsg) {
TLRPC.TL_forumTopic topic = topicsByTopMsgId.get(messageHash(newMsg.id, -newMsg.dialog_id));
if (topic != null) {
topic.topMessage = newMsg;
sortTopics(-newMsg.dialog_id, true);
}
}
private class TopicsLoadOffset {
int lastMessageId;
int lastMessageDate;
int lastTopicId;
}
public static class TopicUpdate {
long dialogId;
int topicId;
int unreadMentions;
int unreadCount;
int topMessageId;
TLRPC.Message topMessage;
ArrayList<MessageObject> groupedMessages;
boolean reloadTopic;
boolean onlyCounters;
}
public void onTopicFragmentResume(long chatId) {
int v = openedTopicsBuChatId.get(chatId, 0);
openedTopicsBuChatId.put(chatId, v + 1);
sortTopics(chatId);
}
public void onTopicFragmentPause(long chatId) {
int v = openedTopicsBuChatId.get(chatId, 0);
v--;
if (v < 0) {
v = 0;
}
openedTopicsBuChatId.put(chatId, v);
}
}

View File

@ -70,8 +70,10 @@ public class UserConfig extends BaseController {
public String premiumGiftsStickerPack;
public String genericAnimationsStickerPack;
public String defaultTopicIcons;
public long lastUpdatedPremiumGiftsStickerPack;
public long lastUpdatedGenericAnimations;
public long lastUpdatedDefaultTopicIcons;
public volatile byte[] savedPasswordHash;
public volatile byte[] savedSaltedPassword;
@ -405,7 +407,7 @@ public class UserConfig extends BaseController {
}
}
private SharedPreferences getPreferences() {
public SharedPreferences getPreferences() {
if (currentAccount == 0) {
return ApplicationLoader.applicationContext.getSharedPreferences("userconfing", Context.MODE_PRIVATE);
} else {

View File

@ -22,6 +22,7 @@ import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -112,32 +113,36 @@ public class Utilities {
if (value == null) {
return 0;
}
int val = 0;
try {
int start = -1, end;
for (end = 0; end < value.length(); ++end) {
char character = value.charAt(end);
boolean allowedChar = character == '-' || character >= '0' && character <= '9';
if (allowedChar && start < 0) {
start = end;
} else if (!allowedChar && start >= 0) {
end++;
break;
if (BuildConfig.BUILD_HOST_IS_WINDOWS) {
Matcher matcher = pattern.matcher(value);
if (matcher.find()) {
return Integer.valueOf(matcher.group());
}
} else {
int val = 0;
try {
int start = -1, end;
for (end = 0; end < value.length(); ++end) {
char character = value.charAt(end);
boolean allowedChar = character == '-' || character >= '0' && character <= '9';
if (allowedChar && start < 0) {
start = end;
} else if (!allowedChar && start >= 0) {
end++;
break;
}
}
}
if (start >= 0) {
String str = value.subSequence(start, end).toString();
if (start >= 0) {
String str = value.subSequence(start, end).toString();
// val = parseInt(str);
val = Integer.parseInt(str);
}
// Matcher matcher = pattern.matcher(value);
// if (matcher.find()) {
// String num = matcher.group(0);
// val = Integer.parseInt(num);
// }
} catch (Exception ignore) {}
return val;
val = Integer.parseInt(str);
}
} catch (Exception ignore) {}
return val;
}
return 0;
}
private static int parseInt(final String s) {
int num = 0;
boolean negative = true;
@ -464,4 +469,12 @@ public class Utilities {
public static interface Callback<T> {
public void run(T arg);
}
public static <Key, Value> Value getOrDefault(HashMap<Key, Value> map, Key key, Value defaultValue) {
Value v = map.get(key);
if (v == null) {
return defaultValue;
}
return v;
}
}

View File

@ -33,6 +33,7 @@ public class WearReplyReceiver extends BroadcastReceiver {
}
long dialogId = intent.getLongExtra("dialog_id", 0);
int maxId = intent.getIntExtra("max_id", 0);
int topicId = intent.getIntExtra("topic_id", 0);
int currentAccount = intent.getIntExtra("currentAccount", 0);
if (dialogId == 0 || maxId == 0 || !UserConfig.isValidAccount(currentAccount)) {
return;
@ -45,7 +46,7 @@ public class WearReplyReceiver extends BroadcastReceiver {
TLRPC.User user1 = accountInstance.getMessagesStorage().getUserSync(dialogId);
AndroidUtilities.runOnUIThread(() -> {
accountInstance.getMessagesController().putUser(user1, true);
sendMessage(accountInstance, text, dialogId, maxId);
sendMessage(accountInstance, text, dialogId, topicId, maxId);
});
});
return;
@ -57,17 +58,31 @@ public class WearReplyReceiver extends BroadcastReceiver {
TLRPC.Chat chat1 = accountInstance.getMessagesStorage().getChatSync(-dialogId);
AndroidUtilities.runOnUIThread(() -> {
accountInstance.getMessagesController().putChat(chat1, true);
sendMessage(accountInstance, text, dialogId, maxId);
sendMessage(accountInstance, text, dialogId, topicId, maxId);
});
});
return;
}
}
sendMessage(accountInstance, text, dialogId, maxId);
sendMessage(accountInstance, text, dialogId, topicId, maxId);
}
private void sendMessage(AccountInstance accountInstance, CharSequence text, long dialog_id, int max_id) {
accountInstance.getSendMessagesHelper().sendMessage(text.toString(), dialog_id, null, null, null, true, null, null, null, true, 0, null, false);
accountInstance.getMessagesController().markDialogAsRead(dialog_id, max_id, max_id, 0, false, 0, 0, true, 0);
private void sendMessage(AccountInstance accountInstance, CharSequence text, long dialog_id, int topicId, int max_id) {
MessageObject replyToMsgId = null;
if (topicId != 0) {
TLRPC.TL_message topicStartMessage = new TLRPC.TL_message();
topicStartMessage.message = "";
topicStartMessage.id = topicId;
topicStartMessage.peer_id = accountInstance.getMessagesController().getPeer(dialog_id);
topicStartMessage.action = new TLRPC.TL_messageActionTopicCreate();
topicStartMessage.action.title = "";
replyToMsgId = new MessageObject(accountInstance.getCurrentAccount(), topicStartMessage, false, false);
}
accountInstance.getSendMessagesHelper().sendMessage(text.toString(), dialog_id, replyToMsgId, null, null, true, null, null, null, true, 0, null, false);
//TODO handle topics
if (topicId == 0) {
accountInstance.getMessagesController().markDialogAsRead(dialog_id, max_id, max_id, 0, false, topicId, 0, true, 0);
}
}
}

View File

@ -44,6 +44,7 @@ import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.AlertDialog;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.LaunchActivity;
import org.w3c.dom.Text;
import java.lang.ref.WeakReference;
import java.net.URLEncoder;
@ -181,11 +182,35 @@ public class Browser {
return url.matches("^(https" + (forceHttps ? "" : "?") + "://)?(te\\.?legra\\.ph|graph\\.org).*"); // telegra.ph, te.legra.ph, graph.org
}
public static String extractUsername(String link) {
if (link == null || TextUtils.isEmpty(link)) {
return null;
}
if (link.startsWith("@")) {
return link.substring(1);
}
if (link.startsWith("t.me/")) {
return link.substring(5);
}
if (link.startsWith("http://t.me/")) {
return link.substring(12);
}
if (link.startsWith("https://t.me/")) {
return link.substring(13);
}
Matcher prefixMatcher = LaunchActivity.PREFIX_T_ME_PATTERN.matcher(link);
if (prefixMatcher.find()) {
return prefixMatcher.group(1);
}
return null;
}
public static boolean urlMustNotHaveConfirmation(String url) {
return (
isTelegraphUrl(url, false, true) ||
url.matches("^(https://)?t\\.me/iv\\??.*") || // t.me/iv?
url.matches("^(https://)?telegram\\.org/(blog|tour)/?.*") // telegram.org/blog, telegram.org/tour
url.matches("^(https://)?telegram\\.org/(blog|tour)/?.*") || // telegram.org/blog, telegram.org/tour
url.matches("^(https://)?fragment\\.com/?.*") // fragment.com
);
}

View File

@ -546,7 +546,7 @@ public class CameraView extends FrameLayout implements TextureView.SurfaceTextur
return x;
}
public void focusToPoint(int x, int y) {
public void focusToPoint(int x, int y, boolean visible) {
Rect focusRect = calculateTapArea(x, y, 1f);
Rect meteringRect = calculateTapArea(x, y, 1.5f);
@ -554,13 +554,19 @@ public class CameraView extends FrameLayout implements TextureView.SurfaceTextur
cameraSession.focusToRect(focusRect, meteringRect);
}
focusProgress = 0.0f;
innerAlpha = 1.0f;
outerAlpha = 1.0f;
cx = x;
cy = y;
lastDrawTime = System.currentTimeMillis();
invalidate();
if (visible) {
focusProgress = 0.0f;
innerAlpha = 1.0f;
outerAlpha = 1.0f;
cx = x;
cy = y;
lastDrawTime = System.currentTimeMillis();
invalidate();
}
}
public void focusToPoint(int x, int y) {
focusToPoint(x, y, true);
}
public void setZoom(float value) {

View File

@ -74,22 +74,40 @@ public class BitmapsCache {
public void createCache() {
try {
long time = System.currentTimeMillis();
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
if (file.exists()) {
RandomAccessFile randomAccessFile = null;
try {
randomAccessFile = new RandomAccessFile(file, "r");
cacheCreated = randomAccessFile.readBoolean();
int framesCount = randomAccessFile.readInt();
if (framesCount == 0) {
cacheCreated = false;
}
if (cacheCreated) {
randomAccessFile.close();
return;
} else {
file.delete();
}
} catch (Exception e) {
} catch (Throwable e) {
try {
file.delete();
} catch (Throwable e2) {
}
} finally {
if (randomAccessFile != null) {
try {
randomAccessFile.close();
} catch (Throwable e2) {
}
}
}
}
randomAccessFile.close();
randomAccessFile = new RandomAccessFile(file, "rw");
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
Bitmap[] bitmap = new Bitmap[N];
ByteArrayOutputStream[] byteArrayOutputStream = new ByteArrayOutputStream[N];
@ -267,10 +285,15 @@ public class BitmapsCache {
return cacheCreated;
}
RandomAccessFile randomAccessFile = null;
int framesCount;
try {
synchronized (mutex) {
randomAccessFile = new RandomAccessFile(file, "r");
cacheCreated = randomAccessFile.readBoolean();
framesCount = randomAccessFile.readInt();
if (framesCount <= 0) {
cacheCreated = false;
}
}
} catch (Exception e) {
@ -283,7 +306,7 @@ public class BitmapsCache {
}
}
}
checkCache = false;
checkCache = true;
return cacheCreated;
}
@ -346,7 +369,7 @@ public class BitmapsCache {
} catch (FileNotFoundException e) {
} catch (Throwable e) {
FileLog.e(e);
FileLog.e(e, false);
}
if (randomAccessFile != null) {

View File

@ -77,6 +77,7 @@ public class ConnectionsManager extends BaseController {
public final static int RequestFlagForceDownload = 32;
public final static int RequestFlagInvokeAfter = 64;
public final static int RequestFlagNeedQuickAck = 128;
public final static int RequestFlagDoNotWaitFloodWait = 1024;
public final static int ConnectionStateConnecting = 1;
public final static int ConnectionStateWaitingForNetwork = 2;
@ -1296,7 +1297,7 @@ public class ConnectionsManager extends BaseController {
buffer.writeBytes(bytes);
return buffer;
} catch (Throwable e) {
FileLog.e(e);
FileLog.e(e, false);
} finally {
try {
if (httpConnectionStream != null) {
@ -1392,7 +1393,7 @@ public class ConnectionsManager extends BaseController {
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null, null, null);
currentTask = task;
});
FileLog.e(e);
FileLog.e(e, false);
}
return null;
}

View File

@ -587,6 +587,14 @@ public class NativeByteBuffer extends AbstractSerializedData {
return buffer.remaining();
}
@Override
protected void finalize() throws Throwable {
if (!reused) {
reuse();
}
super.finalize();
}
public static native long native_getFreeBuffer(int length);
public static native ByteBuffer native_getJavaByteBuffer(long address);
public static native int native_limit(long address);

File diff suppressed because it is too large Load Diff

View File

@ -25,7 +25,6 @@ import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.Log;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
@ -53,28 +52,24 @@ import org.telegram.messenger.SharedConfig;
import org.telegram.ui.Components.BackButtonMenu;
import org.telegram.ui.Components.Bulletin;
import org.telegram.ui.Components.CubicBezierInterpolator;
import org.telegram.ui.Components.FloatingDebug.FloatingDebugController;
import org.telegram.ui.Components.FloatingDebug.FloatingDebugProvider;
import org.telegram.ui.Components.GroupCallPip;
import org.telegram.ui.Components.LayoutHelper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Collections;
import java.util.List;
public class ActionBarLayout extends FrameLayout {
public interface ActionBarLayoutDelegate {
boolean onPreIme();
boolean needPresentFragment(BaseFragment fragment, boolean removeLast, boolean forceWithoutAnimation, ActionBarLayout layout);
boolean needAddFragmentToStack(BaseFragment fragment, ActionBarLayout layout);
boolean needCloseLastFragment(ActionBarLayout layout);
void onRebuildAllFragments(ActionBarLayout layout, boolean last);
}
public class ActionBarLayout extends FrameLayout implements INavigationLayout, FloatingDebugProvider {
public boolean highlightActionButtons = false;
@Override
public void setHighlightActionButtons(boolean highlightActionButtons) {
this.highlightActionButtons = highlightActionButtons;
}
public class LayoutContainer extends FrameLayout {
private Rect rect = new Rect();
@ -300,33 +295,6 @@ public class ActionBarLayout extends FrameLayout {
}
}
public static class ThemeAnimationSettings {
public final Theme.ThemeInfo theme;
public final int accentId;
public final boolean nightTheme;
public final boolean instant;
public boolean onlyTopFragment;
public boolean applyTheme = true;
public Runnable afterStartDescriptionsAddedRunnable;
public Runnable beforeAnimationRunnable;
public Runnable afterAnimationRunnable;
public onAnimationProgress animationProgress;
public long duration = 200;
public Theme.ResourcesProvider resourcesProvider;
public ThemeAnimationSettings(Theme.ThemeInfo theme, int accentId, boolean nightTheme, boolean instant) {
this.theme = theme;
this.accentId = accentId;
this.nightTheme = nightTheme;
this.instant = instant;
}
public interface onAnimationProgress {
void setProgress(float p);
}
}
private static Drawable headerShadowDrawable;
private static Drawable layerShadowDrawable;
private static Paint scrimPaint;
@ -405,11 +373,11 @@ public class ActionBarLayout extends FrameLayout {
private int titleOverlayTextId;
private Runnable overlayAction;
private ActionBarLayoutDelegate delegate;
private INavigationLayoutDelegate delegate;
protected Activity parentActivity;
public ArrayList<BaseFragment> fragmentsStack;
public ArrayList<BackButtonMenu.PulledDialog> pulledDialogs;
private List<BaseFragment> fragmentsStack;
private List<BackButtonMenu.PulledDialog> pulledDialogs;
private Rect rect = new Rect();
private boolean delayedAnimationResumed;
private Runnable onFragmentStackChangedListener;
@ -427,7 +395,8 @@ public class ActionBarLayout extends FrameLayout {
}
}
public void init(ArrayList<BaseFragment> stack) {
@Override
public void setFragmentStack(List<BaseFragment> stack) {
this.fragmentsStack = stack;
this.containerViewBack = new LayoutContainer(parentActivity);
addView(containerViewBack);
@ -464,18 +433,30 @@ public class ActionBarLayout extends FrameLayout {
}
}
public void drawHeaderShadow(Canvas canvas, int y) {
drawHeaderShadow(canvas, 255, y);
private int[] measureSpec = new int[2];
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (delegate != null) {
measureSpec[0] = widthMeasureSpec;
measureSpec[1] = heightMeasureSpec;
delegate.onMeasureOverride(measureSpec);
widthMeasureSpec = measureSpec[0];
heightMeasureSpec = measureSpec[1];
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
public void setInBubbleMode(boolean value) {
inBubbleMode = value;
}
@Override
public boolean isInBubbleMode() {
return inBubbleMode;
}
@Override
public void drawHeaderShadow(Canvas canvas, int alpha, int y) {
if (headerShadowDrawable != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
@ -522,13 +503,7 @@ public class ActionBarLayout extends FrameLayout {
return innerTranslationX;
}
public void dismissDialogs() {
if (!fragmentsStack.isEmpty()) {
BaseFragment lastFragment = fragmentsStack.get(fragmentsStack.size() - 1);
lastFragment.dismissCurrentDialog();
}
}
@Override
public void onResume() {
if (transitionAnimationInProgress) {
if (currentAnimation != null) {
@ -555,6 +530,7 @@ public class ActionBarLayout extends FrameLayout {
}
}
@Override
public void onUserLeaveHint() {
if (!fragmentsStack.isEmpty()) {
BaseFragment lastFragment = fragmentsStack.get(fragmentsStack.size() - 1);
@ -562,6 +538,7 @@ public class ActionBarLayout extends FrameLayout {
}
}
@Override
public void onPause() {
if (!fragmentsStack.isEmpty()) {
BaseFragment lastFragment = fragmentsStack.get(fragmentsStack.size() - 1);
@ -646,6 +623,7 @@ public class ActionBarLayout extends FrameLayout {
invalidate();
}
@Override
public float getCurrentPreviewFragmentAlpha() {
if (inPreviewMode || transitionAnimationPreviewMode || previewOpenAnimationInProgress) {
return (oldFragment != null && oldFragment.inPreviewMode ? containerViewBack : containerView).getAlpha();
@ -654,6 +632,7 @@ public class ActionBarLayout extends FrameLayout {
}
}
@Override
public void drawCurrentPreviewFragment(Canvas canvas, Drawable foregroundDrawable) {
if (inPreviewMode || transitionAnimationPreviewMode || previewOpenAnimationInProgress) {
final ViewGroup v = oldFragment != null && oldFragment.inPreviewMode ? containerViewBack : containerView;
@ -697,8 +676,9 @@ public class ActionBarLayout extends FrameLayout {
}
}
public void setDelegate(ActionBarLayoutDelegate actionBarLayoutDelegate) {
delegate = actionBarLayoutDelegate;
@Override
public void setDelegate(INavigationLayoutDelegate INavigationLayoutDelegate) {
delegate = INavigationLayoutDelegate;
}
private void onSlideAnimationEnd(final boolean backAnimation) {
@ -807,6 +787,7 @@ public class ActionBarLayout extends FrameLayout {
lastFragment.prepareFragmentToSlide(false, true);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (!checkTransitionAnimation() && !inActionMode && !animationInProgress) {
if (fragmentsStack.size() > 1) {
@ -944,6 +925,7 @@ public class ActionBarLayout extends FrameLayout {
return false;
}
@Override
public void onBackPressed() {
if (transitionAnimationPreviewMode || startedTracking || checkTransitionAnimation() || fragmentsStack.isEmpty()) {
return;
@ -963,6 +945,7 @@ public class ActionBarLayout extends FrameLayout {
}
}
@Override
public void onLowMemory() {
for (BaseFragment fragment : fragmentsStack) {
fragment.onLowMemory();
@ -1002,6 +985,7 @@ public class ActionBarLayout extends FrameLayout {
return fragmentsStack.get(fragmentsStack.size() - 1);
}
@Override
public boolean checkTransitionAnimation() {
if (transitionAnimationPreviewMode) {
return false;
@ -1012,10 +996,17 @@ public class ActionBarLayout extends FrameLayout {
return transitionAnimationInProgress;
}
@Override
public boolean isPreviewOpenAnimationInProgress() {
return previewOpenAnimationInProgress;
}
@Override
public boolean isSwipeInProgress() {
return startedTracking;
}
@Override
public boolean isTransitionAnimationInProgress() {
return transitionAnimationInProgress || animationInProgress;
}
@ -1058,22 +1049,6 @@ public class ActionBarLayout extends FrameLayout {
containerViewBack.setVisibility(View.INVISIBLE);
}
public boolean presentFragmentAsPreview(BaseFragment fragment) {
return presentFragment(fragment, false, false, true, true, null);
}
public boolean presentFragmentAsPreviewWithMenu(BaseFragment fragment, ActionBarPopupWindow.ActionBarPopupWindowLayout menu) {
return presentFragment(fragment, false, false, true, true, menu);
}
public boolean presentFragment(BaseFragment fragment) {
return presentFragment(fragment, false, false, true, false, null);
}
public boolean presentFragment(BaseFragment fragment, boolean removeLast) {
return presentFragment(fragment, removeLast, false, true, false, null);
}
private void startLayoutAnimation(final boolean open, final boolean first, final boolean preview) {
if (first) {
animationProgress = 0.0f;
@ -1166,6 +1141,7 @@ public class ActionBarLayout extends FrameLayout {
});
}
@Override
public void resumeDelayedFragmentAnimation() {
delayedAnimationResumed = true;
if (delayedOpenAnimationRunnable == null || waitingForKeyboardCloseRunnable != null) {
@ -1180,6 +1156,7 @@ public class ActionBarLayout extends FrameLayout {
return inPreviewMode || transitionAnimationPreviewMode;
}
@Override
public boolean isInPassivePreviewMode() {
return (inPreviewMode && previewMenu == null) || transitionAnimationPreviewMode;
}
@ -1188,13 +1165,16 @@ public class ActionBarLayout extends FrameLayout {
return isInPreviewMode() && previewMenu != null;
}
@Override
public boolean presentFragment(NavigationParams params) {
BaseFragment fragment = params.fragment;
boolean removeLast = params.removeLast;
boolean forceWithoutAnimation = params.noAnimation;
boolean check = params.checkPresentFromDelegate;
boolean preview = params.preview;
ActionBarPopupWindow.ActionBarPopupWindowLayout menu = params.menuView;
public boolean presentFragment(final BaseFragment fragment, final boolean removeLast, boolean forceWithoutAnimation, boolean check, final boolean preview) {
return presentFragment(fragment, removeLast, forceWithoutAnimation, check, preview, null);
}
public boolean presentFragment(final BaseFragment fragment, final boolean removeLast, boolean forceWithoutAnimation, boolean check, final boolean preview, ActionBarPopupWindow.ActionBarPopupWindowLayout menu) {
if (fragment == null || checkTransitionAnimation() || delegate != null && check && !delegate.needPresentFragment(fragment, removeLast, forceWithoutAnimation, this) || !fragment.onFragmentCreate()) {
if (fragment == null || checkTransitionAnimation() || delegate != null && check && !delegate.needPresentFragment(this, params) || !fragment.onFragmentCreate()) {
return false;
}
if (inPreviewMode && transitionAnimationPreviewMode) {
@ -1489,6 +1469,12 @@ public class ActionBarLayout extends FrameLayout {
return true;
}
@Override
public List<BaseFragment> getFragmentStack() {
return fragmentsStack;
}
@Override
public void setFragmentStackChangedListener(Runnable onFragmentStackChanged) {
this.onFragmentStackChangedListener = onFragmentStackChanged;
}
@ -1500,10 +1486,7 @@ public class ActionBarLayout extends FrameLayout {
ImageLoader.getInstance().onFragmentStackChanged();
}
public boolean addFragmentToStack(BaseFragment fragment) {
return addFragmentToStack(fragment, -1);
}
@Override
public boolean addFragmentToStack(BaseFragment fragment, int position) {
if (delegate != null && !delegate.needAddFragmentToStack(fragment, this) || !fragment.onFragmentCreate()) {
return false;
@ -1548,6 +1531,7 @@ public class ActionBarLayout extends FrameLayout {
onFragmentStackChanged();
}
@Override
public void movePreviewFragment(float dy) {
if (!inPreviewMode || previewMenu != null || transitionAnimationPreviewMode) {
return;
@ -1566,6 +1550,7 @@ public class ActionBarLayout extends FrameLayout {
}
}
@Override
public void expandPreviewFragment() {
previewOpenAnimationInProgress = true;
inPreviewMode = false;
@ -1604,6 +1589,7 @@ public class ActionBarLayout extends FrameLayout {
fragment.setInMenuMode(false);
}
@Override
public void finishPreviewFragment() {
if (!inPreviewMode && !transitionAnimationPreviewMode) {
return;
@ -1803,7 +1789,8 @@ public class ActionBarLayout extends FrameLayout {
}
}
public void showFragment(int i) {
@Override
public void bringToFront(int i) {
if (fragmentsStack.isEmpty()) {
return;
}
@ -1859,7 +1846,7 @@ public class ActionBarLayout extends FrameLayout {
if (fragmentsStack.isEmpty()) {
return;
}
showFragment(fragmentsStack.size() - 1);
bringToFront(fragmentsStack.size() - 1);
}
private void removeFragmentFromStackInternal(BaseFragment fragment) {
@ -1870,13 +1857,7 @@ public class ActionBarLayout extends FrameLayout {
onFragmentStackChanged();
}
public void removeFragmentFromStack(int num) {
if (num >= fragmentsStack.size()) {
return;
}
removeFragmentFromStackInternal(fragmentsStack.get(num));
}
@Override
public void removeFragmentFromStack(BaseFragment fragment) {
if (useAlphaAnimations && fragmentsStack.size() == 1 && AndroidUtilities.isTablet()) {
closeLastFragment(true);
@ -1941,9 +1922,13 @@ public class ActionBarLayout extends FrameLayout {
if (animationProgressListener != null) {
animationProgressListener.setProgress(value);
}
if (delegate != null) {
delegate.onThemeProgress(value);
}
}
@Keep
@Override
public float getThemeAnimationValue() {
return themeAnimationValue;
}
@ -1976,14 +1961,7 @@ public class ActionBarLayout extends FrameLayout {
}
}
public void animateThemedValues(Theme.ThemeInfo theme, int accentId, boolean nightTheme, boolean instant) {
animateThemedValues(new ThemeAnimationSettings(theme, accentId, nightTheme, instant), null);
}
public void animateThemedValues(Theme.ThemeInfo theme, int accentId, boolean nightTheme, boolean instant, Runnable onDone) {
animateThemedValues(new ThemeAnimationSettings(theme, accentId, nightTheme, instant), onDone);
}
@Override
public void animateThemedValues(ThemeAnimationSettings settings, Runnable onDone) {
if (transitionAnimationInProgress || startedTracking) {
animateThemeAfterAnimation = true;
@ -2135,6 +2113,7 @@ public class ActionBarLayout extends FrameLayout {
}
}
@Override
public void rebuildLogout() {
containerView.removeAllViews();
containerViewBack.removeAllViews();
@ -2143,6 +2122,7 @@ public class ActionBarLayout extends FrameLayout {
oldFragment = null;
}
@Override
public void rebuildAllFragmentViews(boolean last, boolean showLastAfter) {
if (transitionAnimationInProgress || startedTracking) {
rebuildAfterAnimation = true;
@ -2169,6 +2149,7 @@ public class ActionBarLayout extends FrameLayout {
}
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU && !checkTransitionAnimation() && !startedTracking && currentActionBar != null) {
currentActionBar.onMenuButtonPressed();
@ -2176,6 +2157,7 @@ public class ActionBarLayout extends FrameLayout {
return super.onKeyUp(keyCode, event);
}
@Override
public void onActionModeStarted(Object mode) {
if (currentActionBar != null) {
currentActionBar.setVisibility(GONE);
@ -2183,6 +2165,7 @@ public class ActionBarLayout extends FrameLayout {
inActionMode = true;
}
@Override
public void onActionModeFinished(Object mode) {
if (currentActionBar != null) {
currentActionBar.setVisibility(VISIBLE);
@ -2232,6 +2215,7 @@ public class ActionBarLayout extends FrameLayout {
}
}
@Override
public void startActivityForResult(final Intent intent, final int requestCode) {
if (parentActivity == null) {
return;
@ -2253,14 +2237,37 @@ public class ActionBarLayout extends FrameLayout {
}
}
@Override
public Theme.MessageDrawable getMessageDrawableOutStart() {
return messageDrawableOutStart;
}
@Override
public Theme.MessageDrawable getMessageDrawableOutMediaStart() {
return messageDrawableOutMediaStart;
}
@Override
public List<BackButtonMenu.PulledDialog> getPulledDialogs() {
return pulledDialogs;
}
@Override
public void setPulledDialogs(List<BackButtonMenu.PulledDialog> pulledDialogs) {
this.pulledDialogs = pulledDialogs;
}
@Override
public void setUseAlphaAnimations(boolean value) {
useAlphaAnimations = value;
}
@Override
public void setBackgroundView(View view) {
backgroundView = view;
}
@Override
public void setDrawerLayoutContainer(DrawerLayoutContainer layout) {
drawerLayoutContainer = layout;
}
@ -2273,6 +2280,7 @@ public class ActionBarLayout extends FrameLayout {
removeActionBarExtraHeight = value;
}
@Override
public void setTitleOverlayText(String title, int titleId, Runnable action) {
titleOverlayText = title;
titleOverlayTextId = titleId;
@ -2285,6 +2293,7 @@ public class ActionBarLayout extends FrameLayout {
}
}
@Override
public boolean extendActionMode(Menu menu) {
return !fragmentsStack.isEmpty() && fragmentsStack.get(fragmentsStack.size() - 1).extendActionMode(menu);
}
@ -2294,12 +2303,44 @@ public class ActionBarLayout extends FrameLayout {
return false;
}
@Override
public void setFragmentPanTranslationOffset(int offset) {
if (containerView != null) {
containerView.setFragmentPanTranslationOffset(offset);
}
}
@Override
public ViewGroup getOverlayContainerView() {
return this;
}
@Override
public List<FloatingDebugController.DebugItem> onGetDebugItems() {
BaseFragment fragment = getLastFragment();
if (fragment != null) {
List<FloatingDebugController.DebugItem> items = new ArrayList<>();
if (fragment instanceof FloatingDebugProvider) {
items.addAll(((FloatingDebugProvider) fragment).onGetDebugItems());
}
observeDebugItemsFromView(items, fragment.getFragmentView());
return items;
}
return Collections.emptyList();
}
private void observeDebugItemsFromView(List<FloatingDebugController.DebugItem> items, View v) {
if (v instanceof FloatingDebugProvider) {
items.addAll(((FloatingDebugProvider) v).onGetDebugItems());
}
if (v instanceof ViewGroup) {
ViewGroup vg = (ViewGroup) v;
for (int i = 0; i < vg.getChildCount(); i++) {
observeDebugItemsFromView(items, vg.getChildAt(i));
}
}
}
private View findScrollingChild(ViewGroup parent, float x, float y) {
int n = parent.getChildCount();
for (int i = 0; i < n; i++) {
@ -2322,34 +2363,4 @@ public class ActionBarLayout extends FrameLayout {
return null;
}
private class StartColorsProvider implements Theme.ResourcesProvider {
HashMap<String, Integer> colors = new HashMap<>();
String[] keysToSave = new String[]{
Theme.key_chat_outBubble,
Theme.key_chat_outBubbleGradient1,
Theme.key_chat_outBubbleGradient2,
Theme.key_chat_outBubbleGradient3,
Theme.key_chat_outBubbleGradientAnimated,
Theme.key_chat_outBubbleShadow
};
@Override
public Integer getColor(String key) {
return colors.get(key);
}
@Override
public Integer getCurrentColor(String key) {
return colors.get(key);
}
public void saveColors(Theme.ResourcesProvider fragmentResourceProvider) {
colors.clear();
for (String key : keysToSave) {
colors.put(key, fragmentResourceProvider.getCurrentColor(key));
}
}
}
}

View File

@ -29,7 +29,6 @@ import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.transition.TransitionValues;
import android.transition.Visibility;
import android.util.Log;
import android.util.TypedValue;
import android.view.ActionMode;
import android.view.Gravity;
@ -60,7 +59,6 @@ import org.telegram.messenger.LocaleController;
import org.telegram.messenger.NotificationCenter;
import org.telegram.messenger.R;
import org.telegram.messenger.UserConfig;
import org.telegram.messenger.XiaomiUtilities;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.Adapters.FiltersView;
import org.telegram.ui.Components.BackupImageView;
@ -78,6 +76,15 @@ public class ActionBarMenuItem extends FrameLayout {
private FrameLayout wrappedSearchFrameLayout;
public void setSearchPaddingStart(int padding) {
searchItemPaddingStart = padding;
if (searchContainer != null) {
((MarginLayoutParams)searchContainer.getLayoutParams()).leftMargin = AndroidUtilities.dp(padding);
searchContainer.setClipChildren(searchItemPaddingStart != 0);
}
}
public static class ActionBarMenuItemSearchListener {
public void onSearchExpand() {
}
@ -171,6 +178,7 @@ public class ActionBarMenuItem extends FrameLayout {
private float transitionOffset;
private View showSubMenuFrom;
private final Theme.ResourcesProvider resourcesProvider;
public int searchItemPaddingStart;
private OnClickListener onClickListener;
@ -711,9 +719,9 @@ public class ActionBarMenuItem extends FrameLayout {
}
});
// if (measurePopup) {
container.measure(MeasureSpec.makeMeasureSpec(AndroidUtilities.displaySize.x - AndroidUtilities.dp(40), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.displaySize.y, MeasureSpec.AT_MOST));
measurePopup = false;
// if (measurePopup) {
container.measure(MeasureSpec.makeMeasureSpec(AndroidUtilities.displaySize.x - AndroidUtilities.dp(40), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.displaySize.y, MeasureSpec.AT_MOST));
measurePopup = false;
//}
processedPopupClick = false;
popupWindow.setFocusable(true);
@ -889,35 +897,35 @@ public class ActionBarMenuItem extends FrameLayout {
ChangeBounds changeBounds = new ChangeBounds();
changeBounds.setDuration(150);
transition.addTransition(new Visibility() {
@Override
public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) {
if (view instanceof SearchFilterView) {
AnimatorSet set = new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1f),
ObjectAnimator.ofFloat(view, View.SCALE_X, 0.5f, 1f),
ObjectAnimator.ofFloat(view, View.SCALE_Y, 0.5f, 1f)
);
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
return set;
}
return ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1f);
}
@Override
public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) {
if (view instanceof SearchFilterView) {
AnimatorSet set = new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(view, View.ALPHA, view.getAlpha(), 0f),
ObjectAnimator.ofFloat(view, View.SCALE_X, view.getScaleX(), 0.5f),
ObjectAnimator.ofFloat(view, View.SCALE_Y, view.getScaleX(), 0.5f)
);
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
return set;
}
return ObjectAnimator.ofFloat(view, View.ALPHA, 1f, 0);
}
}.setDuration(150)).addTransition(changeBounds);
@Override
public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) {
if (view instanceof SearchFilterView) {
AnimatorSet set = new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1f),
ObjectAnimator.ofFloat(view, View.SCALE_X, 0.5f, 1f),
ObjectAnimator.ofFloat(view, View.SCALE_Y, 0.5f, 1f)
);
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
return set;
}
return ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1f);
}
@Override
public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) {
if (view instanceof SearchFilterView) {
AnimatorSet set = new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(view, View.ALPHA, view.getAlpha(), 0f),
ObjectAnimator.ofFloat(view, View.SCALE_X, view.getScaleX(), 0.5f),
ObjectAnimator.ofFloat(view, View.SCALE_Y, view.getScaleX(), 0.5f)
);
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
return set;
}
return ObjectAnimator.ofFloat(view, View.ALPHA, 1f, 0);
}
}.setDuration(150)).addTransition(changeBounds);
transition.setOrdering(TransitionSet.ORDERING_TOGETHER);
transition.setInterpolator(CubicBezierInterpolator.EASE_OUT);
int selectedAccount = UserConfig.selectedAccount;
@ -1194,7 +1202,7 @@ public class ActionBarMenuItem extends FrameLayout {
searchField.layout(x, searchField.getTop(), x + searchField.getMeasuredWidth(), searchField.getBottom());
}
};
searchContainer.setClipChildren(false);
searchContainer.setClipChildren(searchItemPaddingStart != 0);
wrappedSearchFrameLayout = null;
if (wrapInScrollView) {
wrappedSearchFrameLayout = new FrameLayout(getContext());
@ -1232,11 +1240,11 @@ public class ActionBarMenuItem extends FrameLayout {
};
horizontalScrollView.addView(searchContainer, LayoutHelper.createScroll(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, 0));
horizontalScrollView.setHorizontalScrollBarEnabled(false);
horizontalScrollView.setClipChildren(false);
horizontalScrollView.setClipChildren(searchItemPaddingStart != 0);
wrappedSearchFrameLayout.addView(horizontalScrollView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0, 0, 0, 48, 0));
parentMenu.addView(wrappedSearchFrameLayout, 0, LayoutHelper.createLinear(0, LayoutHelper.MATCH_PARENT, 1.0f, 0, 0, 0, 0));
parentMenu.addView(wrappedSearchFrameLayout, 0, LayoutHelper.createLinear(0, LayoutHelper.MATCH_PARENT, 1.0f, searchItemPaddingStart, 0, 0, 0));
} else {
parentMenu.addView(searchContainer, 0, LayoutHelper.createLinear(0, LayoutHelper.MATCH_PARENT, 1.0f, 6, 0, 0, 0));
parentMenu.addView(searchContainer, 0, LayoutHelper.createLinear(0, LayoutHelper.MATCH_PARENT, 1.0f, searchItemPaddingStart + 6, 0, 0, 0));
}
searchContainer.setVisibility(GONE);
@ -1370,11 +1378,11 @@ public class ActionBarMenuItem extends FrameLayout {
searchFilterLayout.setVisibility(View.VISIBLE);
if (!LocaleController.isRTL) {
searchContainer.addView(searchFieldCaption, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 36, Gravity.CENTER_VERTICAL | Gravity.LEFT, 0, 5.5f, 0, 0));
searchContainer.addView(searchField, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 36, Gravity.CENTER_VERTICAL, 6, 0, 48, 0));
searchContainer.addView(searchField, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 36, Gravity.CENTER_VERTICAL, 6, 0, wrapInScrollView ? 0 : 48, 0));
searchContainer.addView(searchFilterLayout, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 32, Gravity.CENTER_VERTICAL, 0, 0, 48, 0));
} else {
searchContainer.addView(searchFilterLayout, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 32, Gravity.CENTER_VERTICAL, 0, 0, 48, 0));
searchContainer.addView(searchField, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 36, Gravity.CENTER_VERTICAL, 0, 0, wrapInScrollView ? 0 : 48, 0));
searchContainer.addView(searchField, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 36, Gravity.CENTER_VERTICAL, 0, 0, wrapInScrollView ? 0 : 48, 0));
searchContainer.addView(searchFieldCaption, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 36, Gravity.CENTER_VERTICAL | Gravity.RIGHT, 0, 5.5f, 48, 0));
}
searchFilterLayout.setClipChildren(false);

View File

@ -15,11 +15,13 @@ import android.animation.ObjectAnimator;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.os.Bundle;
import android.text.TextPaint;
@ -38,17 +40,22 @@ import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.R;
import org.telegram.ui.Components.LayoutHelper;
import org.telegram.ui.Components.LineProgressView;
import org.telegram.ui.Components.RLottieDrawable;
import org.telegram.ui.Components.RLottieImageView;
import org.telegram.ui.Components.RadialProgressView;
import org.telegram.ui.Components.spoilers.SpoilersTextView;
import java.util.ArrayList;
import java.util.Map;
public class AlertDialog extends Dialog implements Drawable.Callback {
@ -88,8 +95,10 @@ public class AlertDialog extends Dialog implements Drawable.Callback {
private CharSequence message;
private int topResId;
private View topView;
private boolean topAnimationIsNew;
private int topAnimationId;
private int topAnimationSize;
private Map<String, Integer> topAnimationLayerColors;
private int topHeight = 132;
private Drawable topDrawable;
private int topBackgroundColor;
@ -139,7 +148,7 @@ public class AlertDialog extends Dialog implements Drawable.Callback {
private ArrayList<AlertDialogCell> itemViews = new ArrayList<>();
private float aspectRatio;
private boolean dimEnabled = true;
private float dimAlpha = 0.6f;
private float dimAlpha = 0.5f;
private boolean dimCustom = false;
private final Theme.ResourcesProvider resourcesProvider;
private boolean topAnimationAutoRepeat = true;
@ -213,7 +222,7 @@ public class AlertDialog extends Dialog implements Drawable.Callback {
backgroundPaddings = new Rect();
if (progressStyle != 3) {
shadowDrawable = context.getResources().getDrawable(R.drawable.popup_fixed_alert).mutate();
shadowDrawable = context.getResources().getDrawable(R.drawable.popup_fixed_alert3).mutate();
shadowDrawable.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_dialogBackground), PorterDuff.Mode.MULTIPLY));
shadowDrawable.getPadding(backgroundPaddings);
}
@ -354,7 +363,7 @@ public class AlertDialog extends Dialog implements Drawable.Callback {
}
}
setMeasuredDimension(width, maxContentHeight - availableHeight + getPaddingTop() + getPaddingBottom());
setMeasuredDimension(width, maxContentHeight - availableHeight + getPaddingTop() + getPaddingBottom() - (topAnimationIsNew ? AndroidUtilities.dp(8) : 0));
inLayout = false;
if (lastScreenWidth != AndroidUtilities.displaySize.x) {
@ -468,11 +477,53 @@ public class AlertDialog extends Dialog implements Drawable.Callback {
} else {
topImageView.setAutoRepeat(topAnimationAutoRepeat);
topImageView.setAnimation(topAnimationId, topAnimationSize, topAnimationSize);
if (topAnimationLayerColors != null) {
RLottieDrawable drawable = topImageView.getAnimatedDrawable();
for (Map.Entry<String, Integer> en : topAnimationLayerColors.entrySet()) {
drawable.setLayerColor(en.getKey(), en.getValue());
}
}
topImageView.playAnimation();
}
topImageView.setScaleType(ImageView.ScaleType.CENTER);
topImageView.setBackgroundDrawable(getContext().getResources().getDrawable(R.drawable.popup_fixed_top));
topImageView.getBackground().setColorFilter(new PorterDuffColorFilter(topBackgroundColor, PorterDuff.Mode.MULTIPLY));
if (topAnimationIsNew) {
GradientDrawable d = new GradientDrawable();
d.setColor(topBackgroundColor);
d.setCornerRadius(AndroidUtilities.dp(128));
topImageView.setBackground(new Drawable() {
int size = topAnimationSize + AndroidUtilities.dp(52);
@Override
public void draw(@NonNull Canvas canvas) {
d.setBounds((int) ((topImageView.getWidth() - size) / 2f), (int) ((topImageView.getHeight() - size) / 2f), (int) ((topImageView.getWidth() + size) / 2f), (int) ((topImageView.getHeight() + size) / 2f));
d.draw(canvas);
}
@Override
public void setAlpha(int alpha) {
d.setAlpha(alpha);
}
@Override
public void setColorFilter(@Nullable ColorFilter colorFilter) {
d.setColorFilter(colorFilter);
}
@Override
public int getOpacity() {
return d.getOpacity();
}
});
topHeight = 92;
} else {
topImageView.setBackgroundDrawable(getContext().getResources().getDrawable(R.drawable.popup_fixed_top));
topImageView.getBackground().setColorFilter(new PorterDuffColorFilter(topBackgroundColor, PorterDuff.Mode.MULTIPLY));
}
if (topAnimationIsNew) {
topImageView.setTranslationY(AndroidUtilities.dp(16));
} else {
topImageView.setTranslationY(0);
}
topImageView.setPadding(0, 0, 0, 0);
containerView.addView(topImageView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, topHeight, Gravity.LEFT | Gravity.TOP, -8, -8, 0, 0));
} else if (topView != null) {
@ -482,15 +533,15 @@ public class AlertDialog extends Dialog implements Drawable.Callback {
if (title != null) {
titleContainer = new FrameLayout(getContext());
containerView.addView(titleContainer, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, 24, 0, 24, 0));
containerView.addView(titleContainer, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, topAnimationIsNew ? Gravity.CENTER_HORIZONTAL : 0, 24, 0, 24, 0));
titleTextView = new SpoilersTextView(getContext(), false);
titleTextView.setText(title);
titleTextView.setTextColor(getThemedColor(Theme.key_dialogTextBlack));
titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20);
titleTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
titleTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP);
titleContainer.addView(titleTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 0, 19, 0, (subtitle != null ? 2 : (items != null ? 14 : 10))));
titleTextView.setGravity((topAnimationIsNew ? Gravity.CENTER_HORIZONTAL : LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP);
titleContainer.addView(titleTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (topAnimationIsNew ? Gravity.CENTER_HORIZONTAL : LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 0, 19, 0, topAnimationIsNew ? 4 : (subtitle != null ? 2 : (items != null ? 14 : 10))));
}
if (secondTitle != null && title != null) {
@ -544,7 +595,7 @@ public class AlertDialog extends Dialog implements Drawable.Callback {
}
messageTextView = new SpoilersTextView(getContext(), false);
messageTextView.setTextColor(getThemedColor(Theme.key_dialogTextBlack));
messageTextView.setTextColor(getThemedColor(topAnimationIsNew ? Theme.key_windowBackgroundWhiteGrayText : Theme.key_dialogTextBlack));
messageTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
messageTextView.setMovementMethod(new AndroidUtilities.LinkMovementMethodMy());
messageTextView.setLinkTextColor(getThemedColor(Theme.key_dialogTextLink));
@ -552,7 +603,7 @@ public class AlertDialog extends Dialog implements Drawable.Callback {
messageTextView.setClickable(false);
messageTextView.setEnabled(false);
}
messageTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP);
messageTextView.setGravity((topAnimationIsNew ? Gravity.CENTER_HORIZONTAL : LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP);
if (progressViewStyle == 1) {
progressViewContainer = new FrameLayout(getContext());
containerView.addView(progressViewContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 44, Gravity.LEFT | Gravity.TOP, 23, title == null ? 24 : 0, 23, 24));
@ -592,7 +643,7 @@ public class AlertDialog extends Dialog implements Drawable.Callback {
progressView.setProgressColor(getThemedColor(Theme.key_dialog_inlineProgress));
progressViewContainer.addView(progressView, LayoutHelper.createLinear(86, 86));
} else {
scrollContainer.addView(messageTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 24, 0, 24, customView != null || items != null ? customViewOffset : 0));
scrollContainer.addView(messageTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (topAnimationIsNew ? Gravity.CENTER_HORIZONTAL : LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 24, 0, 24, customView != null || items != null ? customViewOffset : 0));
}
if (!TextUtils.isEmpty(message)) {
messageTextView.setText(message);
@ -734,6 +785,9 @@ public class AlertDialog extends Dialog implements Drawable.Callback {
}
buttonsLayout.setPadding(AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8));
containerView.addView(buttonsLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 52));
if (topAnimationIsNew) {
buttonsLayout.setTranslationY(-AndroidUtilities.dp(8));
}
if (positiveButtonText != null) {
TextView textView = new TextView(getContext()) {
@ -746,20 +800,20 @@ public class AlertDialog extends Dialog implements Drawable.Callback {
@Override
public void setTextColor(int color) {
super.setTextColor(color);
setBackgroundDrawable(Theme.getRoundRectSelectorDrawable(color));
setBackgroundDrawable(Theme.getRoundRectSelectorDrawable(AndroidUtilities.dp(6), color));
}
};
textView.setMinWidth(AndroidUtilities.dp(64));
textView.setTag(Dialog.BUTTON_POSITIVE);
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
textView.setTextColor(getThemedColor(dialogButtonColorKey));
textView.setGravity(Gravity.CENTER);
textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
// textView.setLines(1);
// textView.setSingleLine(true); //TODO
textView.setText(positiveButtonText.toString().toUpperCase());
textView.setBackgroundDrawable(Theme.getRoundRectSelectorDrawable(getThemedColor(dialogButtonColorKey)));
textView.setPadding(AndroidUtilities.dp(10), 0, AndroidUtilities.dp(10), 0);
textView.setText(positiveButtonText.toString());
textView.setBackgroundDrawable(Theme.getRoundRectSelectorDrawable(AndroidUtilities.dp(6), getThemedColor(dialogButtonColorKey)));
textView.setPadding(AndroidUtilities.dp(12), 0, AndroidUtilities.dp(12), 0);
if (verticalButtons) {
buttonsLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 36, LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT));
} else {
@ -786,20 +840,20 @@ public class AlertDialog extends Dialog implements Drawable.Callback {
@Override
public void setTextColor(int color) {
super.setTextColor(color);
setBackgroundDrawable(Theme.getRoundRectSelectorDrawable(color));
setBackgroundDrawable(Theme.getRoundRectSelectorDrawable(AndroidUtilities.dp(6), color));
}
};
textView.setMinWidth(AndroidUtilities.dp(64));
textView.setTag(Dialog.BUTTON_NEGATIVE);
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
textView.setTextColor(getThemedColor(dialogButtonColorKey));
textView.setGravity(Gravity.CENTER);
textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
textView.setEllipsize(TextUtils.TruncateAt.END);
textView.setSingleLine(true);
textView.setText(negativeButtonText.toString().toUpperCase());
textView.setBackgroundDrawable(Theme.getRoundRectSelectorDrawable(getThemedColor(dialogButtonColorKey)));
textView.setPadding(AndroidUtilities.dp(10), 0, AndroidUtilities.dp(10), 0);
textView.setText(negativeButtonText.toString());
textView.setBackgroundDrawable(Theme.getRoundRectSelectorDrawable(AndroidUtilities.dp(6), getThemedColor(dialogButtonColorKey)));
textView.setPadding(AndroidUtilities.dp(12), 0, AndroidUtilities.dp(12), 0);
if (verticalButtons) {
buttonsLayout.addView(textView, 0, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 36, LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT));
} else {
@ -826,20 +880,20 @@ public class AlertDialog extends Dialog implements Drawable.Callback {
@Override
public void setTextColor(int color) {
super.setTextColor(color);
setBackgroundDrawable(Theme.getRoundRectSelectorDrawable(color));
setBackgroundDrawable(Theme.getRoundRectSelectorDrawable(AndroidUtilities.dp(6), color));
}
};
textView.setMinWidth(AndroidUtilities.dp(64));
textView.setTag(Dialog.BUTTON_NEUTRAL);
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
textView.setTextColor(getThemedColor(dialogButtonColorKey));
textView.setGravity(Gravity.CENTER);
textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
textView.setEllipsize(TextUtils.TruncateAt.END);
textView.setSingleLine(true);
textView.setText(neutralButtonText.toString().toUpperCase());
textView.setBackground(Theme.getRoundRectSelectorDrawable(getThemedColor(dialogButtonColorKey)));
textView.setPadding(AndroidUtilities.dp(10), 0, AndroidUtilities.dp(10), 0);
textView.setText(neutralButtonText.toString());
textView.setBackground(Theme.getRoundRectSelectorDrawable(AndroidUtilities.dp(6), getThemedColor(dialogButtonColorKey)));
textView.setPadding(AndroidUtilities.dp(12), 0, AndroidUtilities.dp(12), 0);
if (verticalButtons) {
buttonsLayout.addView(textView, 1, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 36, LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT));
} else {
@ -1296,10 +1350,20 @@ public class AlertDialog extends Dialog implements Drawable.Callback {
}
public Builder setTopAnimation(int resId, int size, boolean autoRepeat, int backgroundColor) {
return setTopAnimation(resId, size, autoRepeat, backgroundColor, null);
}
public Builder setTopAnimation(int resId, int size, boolean autoRepeat, int backgroundColor, Map<String, Integer> layerColors) {
alertDialog.topAnimationId = resId;
alertDialog.topAnimationSize = size;
alertDialog.topAnimationAutoRepeat = autoRepeat;
alertDialog.topBackgroundColor = backgroundColor;
alertDialog.topAnimationLayerColors = layerColors;
return this;
}
public Builder setTopAnimationIsNew(boolean isNew) {
alertDialog.topAnimationIsNew = isNew;
return this;
}

View File

@ -15,6 +15,7 @@ import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Build;
@ -62,7 +63,7 @@ public abstract class BaseFragment {
protected int currentAccount = UserConfig.selectedAccount;
protected View fragmentView;
protected ActionBarLayout parentLayout;
protected INavigationLayout parentLayout;
protected ActionBar actionBar;
protected boolean inPreviewMode;
protected boolean inMenuMode;
@ -92,6 +93,18 @@ public abstract class BaseFragment {
currentAccount = account;
}
public boolean hasOwnBackground() {
return hasOwnBackground;
}
public void setHasOwnBackground(boolean hasOwnBackground) {
this.hasOwnBackground = hasOwnBackground;
}
public boolean getFragmentBeginToShow() {
return fragmentBeginToShow;
}
public ActionBar getActionBar() {
return actionBar;
}
@ -100,6 +113,10 @@ public abstract class BaseFragment {
return fragmentView;
}
public void setFragmentView(View fragmentView) {
this.fragmentView = fragmentView;
}
public View createView(Context context) {
return null;
}
@ -136,7 +153,7 @@ public abstract class BaseFragment {
return parentLayout != null && parentLayout.isInPassivePreviewMode();
}
protected void setInPreviewMode(boolean value) {
public void setInPreviewMode(boolean value) {
inPreviewMode = value;
if (actionBar != null) {
if (inPreviewMode) {
@ -147,18 +164,18 @@ public abstract class BaseFragment {
}
}
protected void setInMenuMode(boolean value) {
public void setInMenuMode(boolean value) {
inMenuMode = value;
}
protected void onPreviewOpenAnimationEnd() {
public void onPreviewOpenAnimationEnd() {
}
protected boolean hideKeyboardOnShow() {
return true;
}
protected void clearViews() {
public void clearViews() {
if (fragmentView != null) {
ViewGroup parent = (ViewGroup) fragmentView.getParent();
if (parent != null) {
@ -185,16 +202,16 @@ public abstract class BaseFragment {
parentLayout = null;
}
protected void onRemoveFromParent() {
public void onRemoveFromParent() {
}
public void setParentFragment(BaseFragment fragment) {
setParentLayout(fragment.parentLayout);
fragmentView = createView(parentLayout.getContext());
fragmentView = createView(parentLayout.getView().getContext());
}
protected void setParentLayout(ActionBarLayout layout) {
public void setParentLayout(INavigationLayout layout) {
if (parentLayout != layout) {
parentLayout = layout;
inBubbleMode = parentLayout != null && parentLayout.isInBubbleMode();
@ -208,12 +225,12 @@ public abstract class BaseFragment {
FileLog.e(e);
}
}
if (parentLayout != null && parentLayout.getContext() != fragmentView.getContext()) {
if (parentLayout != null && parentLayout.getView().getContext() != fragmentView.getContext()) {
fragmentView = null;
}
}
if (actionBar != null) {
boolean differentParent = parentLayout != null && parentLayout.getContext() != actionBar.getContext();
boolean differentParent = parentLayout != null && parentLayout.getView().getContext() != actionBar.getContext();
if (actionBar.shouldAddToContainer() || differentParent) {
ViewGroup parent = (ViewGroup) actionBar.getParent();
if (parent != null) {
@ -229,7 +246,7 @@ public abstract class BaseFragment {
}
}
if (parentLayout != null && actionBar == null) {
actionBar = createActionBar(parentLayout.getContext());
actionBar = createActionBar(parentLayout.getView().getContext());
if (actionBar != null) {
actionBar.parentFragment = this;
}
@ -340,11 +357,23 @@ public abstract class BaseFragment {
}
}
public void setPaused(boolean paused) {
if (isPaused == paused) {
return;
}
if (paused) {
onPause();
} else {
onResume();
}
}
public BaseFragment getFragmentForAlert(int offset) {
if (parentLayout == null || parentLayout.fragmentsStack.size() <= 1 + offset) {
if (parentLayout == null || parentLayout.getFragmentStack().size() <= 1 + offset) {
return this;
}
return parentLayout.fragmentsStack.get(parentLayout.fragmentsStack.size() - 2 - offset);
return parentLayout.getFragmentStack().get(parentLayout.getFragmentStack().size() - 2 - offset);
}
public void onConfigurationChanged(android.content.res.Configuration newConfig) {
@ -372,10 +401,10 @@ public abstract class BaseFragment {
}
public boolean isLastFragment() {
return parentLayout != null && !parentLayout.fragmentsStack.isEmpty() && parentLayout.fragmentsStack.get(parentLayout.fragmentsStack.size() - 1) == this;
return parentLayout != null && parentLayout.getLastFragment() == this;
}
public ActionBarLayout getParentLayout() {
public INavigationLayout getParentLayout() {
return parentLayout;
}
@ -409,9 +438,13 @@ public abstract class BaseFragment {
return allowPresentFragment() && parentLayout != null && parentLayout.presentFragment(fragment, removeLast, forceWithoutAnimation, true, false, null);
}
public boolean presentFragment(INavigationLayout.NavigationParams params) {
return allowPresentFragment() && parentLayout != null && parentLayout.presentFragment(params);
}
public Activity getParentActivity() {
if (parentLayout != null) {
return parentLayout.parentActivity;
return parentLayout.getParentActivity();
}
return null;
}
@ -467,26 +500,26 @@ public abstract class BaseFragment {
}
}
protected void onSlideProgress(boolean isOpen, float progress) {
public void onSlideProgress(boolean isOpen, float progress) {
}
protected void onTransitionAnimationProgress(boolean isOpen, float progress) {
public void onTransitionAnimationProgress(boolean isOpen, float progress) {
}
protected void onTransitionAnimationStart(boolean isOpen, boolean backward) {
public void onTransitionAnimationStart(boolean isOpen, boolean backward) {
inTransitionAnimation = true;
if (isOpen) {
fragmentBeginToShow = true;
}
}
protected void onTransitionAnimationEnd(boolean isOpen, boolean backward) {
public void onTransitionAnimationEnd(boolean isOpen, boolean backward) {
inTransitionAnimation = false;
}
protected void onBecomeFullyVisible() {
public void onBecomeFullyVisible() {
AccessibilityManager mgr = (AccessibilityManager) ApplicationLoader.applicationContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
if (mgr.isEnabled()) {
ActionBar actionBar = getActionBar();
@ -499,15 +532,15 @@ public abstract class BaseFragment {
}
}
protected int getPreviewHeight() {
public int getPreviewHeight() {
return LayoutHelper.MATCH_PARENT;
}
protected void onBecomeFullyHidden() {
public void onBecomeFullyHidden() {
}
protected AnimatorSet onCustomTransitionAnimation(boolean isOpen, final Runnable callback) {
public AnimatorSet onCustomTransitionAnimation(boolean isOpen, Runnable callback) {
return null;
}
@ -524,7 +557,7 @@ public abstract class BaseFragment {
}
public Dialog showDialog(Dialog dialog, boolean allowInTransition, final Dialog.OnDismissListener onDismissListener) {
if (dialog == null || parentLayout == null || parentLayout.animationInProgress || parentLayout.startedTracking || !allowInTransition && parentLayout.checkTransitionAnimation()) {
if (dialog == null || parentLayout == null || parentLayout.isTransitionAnimationInProgress() || parentLayout.isSwipeInProgress() || !allowInTransition && parentLayout.checkTransitionAnimation()) {
return null;
}
try {
@ -669,7 +702,7 @@ public abstract class BaseFragment {
return false;
}
protected void prepareFragmentToSlide(boolean topFragment, boolean beginSlide) {
public void prepareFragmentToSlide(boolean topFragment, boolean beginSlide) {
}
@ -677,18 +710,18 @@ public abstract class BaseFragment {
}
public ActionBarLayout[] showAsSheet(BaseFragment fragment) {
public INavigationLayout[] showAsSheet(BaseFragment fragment) {
if (getParentActivity() == null) {
return null;
}
ActionBarLayout[] actionBarLayout = new ActionBarLayout[]{new ActionBarLayout(getParentActivity())};
INavigationLayout[] actionBarLayout = new INavigationLayout[]{INavigationLayout.newLayout(getParentActivity())};
BottomSheet bottomSheet = new BottomSheet(getParentActivity(), true) {
{
actionBarLayout[0].init(new ArrayList<>());
actionBarLayout[0].setFragmentStack(new ArrayList<>());
actionBarLayout[0].addFragmentToStack(fragment);
actionBarLayout[0].showLastFragment();
actionBarLayout[0].setPadding(backgroundPaddingLeft, 0, backgroundPaddingLeft, 0);
containerView = actionBarLayout[0];
actionBarLayout[0].getView().setPadding(backgroundPaddingLeft, 0, backgroundPaddingLeft, 0);
containerView = actionBarLayout[0].getView();
setApplyBottomPadding(false);
setApplyBottomPadding(false);
setOnDismissListener(dialog -> fragment.onFragmentDestroy());
@ -701,7 +734,7 @@ public abstract class BaseFragment {
@Override
public void onBackPressed() {
if (actionBarLayout[0] == null || actionBarLayout[0].fragmentsStack.size() <= 1) {
if (actionBarLayout[0] == null || actionBarLayout[0].getFragmentStack().size() <= 1) {
super.onBackPressed();
} else {
actionBarLayout[0].onBackPressed();
@ -791,4 +824,8 @@ public abstract class BaseFragment {
}
return ColorUtils.calculateLuminance(color) > 0.7f;
}
public void drawOverlay(Canvas canvas, View parent) {
}
}

View File

@ -1599,7 +1599,8 @@ public class BottomSheet extends Dialog {
try {
super.dismiss();
} catch (Exception e) {
FileLog.e(e);
//ignore: not attached to window manager
FileLog.e(e, false);
}
}

View File

@ -51,7 +51,7 @@ public class DrawerLayoutContainer extends FrameLayout {
private static final int MIN_DRAWER_MARGIN = 64;
private ViewGroup drawerLayout;
private ActionBarLayout parentActionBarLayout;
private INavigationLayout parentActionBarLayout;
private boolean maybeStartTracking;
private boolean startedTracking;
@ -204,8 +204,8 @@ public class DrawerLayoutContainer extends FrameLayout {
if (drawerLayout.getVisibility() != newVisibility) {
drawerLayout.setVisibility(newVisibility);
}
if (!parentActionBarLayout.fragmentsStack.isEmpty()) {
BaseFragment currentFragment = parentActionBarLayout.fragmentsStack.get(0);
if (!parentActionBarLayout.getFragmentStack().isEmpty()) {
BaseFragment currentFragment = parentActionBarLayout.getFragmentStack().get(0);
if (drawerPosition == drawerLayout.getMeasuredWidth()) {
currentFragment.setProgressToDrawerOpened(1f);
} else if (drawerPosition == 0) {
@ -233,8 +233,8 @@ public class DrawerLayoutContainer extends FrameLayout {
if (!allowOpenDrawer || drawerLayout == null) {
return;
}
if (AndroidUtilities.isTablet() && parentActionBarLayout != null && parentActionBarLayout.parentActivity != null) {
AndroidUtilities.hideKeyboard(parentActionBarLayout.parentActivity.getCurrentFocus());
if (AndroidUtilities.isTablet() && parentActionBarLayout != null && parentActionBarLayout.getParentActivity() != null) {
AndroidUtilities.hideKeyboard(parentActionBarLayout.getParentActivity().getCurrentFocus());
}
cancelCurrentAnimation();
AnimatorSet animatorSet = new AnimatorSet();
@ -307,7 +307,7 @@ public class DrawerLayoutContainer extends FrameLayout {
return drawerLayout;
}
public void setParentActionBarLayout(ActionBarLayout layout) {
public void setParentActionBarLayout(INavigationLayout layout) {
parentActionBarLayout = layout;
}
@ -432,13 +432,13 @@ public class DrawerLayoutContainer extends FrameLayout {
return true;
}
if ((allowOpenDrawerBySwipe || drawerOpened) && allowOpenDrawer && parentActionBarLayout.fragmentsStack.size() == 1) {
if ((allowOpenDrawerBySwipe || drawerOpened) && allowOpenDrawer && parentActionBarLayout.getFragmentStack().size() == 1) {
if (ev != null && (ev.getAction() == MotionEvent.ACTION_DOWN || ev.getAction() == MotionEvent.ACTION_MOVE) && !startedTracking && !maybeStartTracking) {
View scrollingChild = findScrollingChild(this, ev.getX(),ev.getY());
if (scrollingChild != null) {
return false;
}
parentActionBarLayout.getHitRect(rect);
parentActionBarLayout.getView().getHitRect(rect);
startedTrackingX = (int) ev.getX();
startedTrackingY = (int) ev.getY();
if (rect.contains(startedTrackingX, startedTrackingY)) {

View File

@ -0,0 +1,379 @@
package org.telegram.ui.ActionBar;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import org.telegram.messenger.SharedConfig;
import org.telegram.ui.Components.BackButtonMenu;
import org.telegram.ui.LNavigation.LNavigation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public interface INavigationLayout {
int REBUILD_FLAG_REBUILD_LAST = 1, REBUILD_FLAG_REBUILD_ONLY_LAST = 2;
boolean presentFragment(NavigationParams params);
boolean checkTransitionAnimation();
boolean addFragmentToStack(BaseFragment fragment, int position);
void removeFragmentFromStack(BaseFragment fragment);
List<BaseFragment> getFragmentStack();
void setDelegate(INavigationLayoutDelegate INavigationLayoutDelegate);
void closeLastFragment(boolean animated, boolean forceNoAnimation);
DrawerLayoutContainer getDrawerLayoutContainer();
void setDrawerLayoutContainer(DrawerLayoutContainer drawerLayoutContainer);
void setRemoveActionBarExtraHeight(boolean removeExtraHeight);
void setTitleOverlayText(String title, int titleId, Runnable action);
void animateThemedValues(ThemeAnimationSettings settings, Runnable onDone);
float getThemeAnimationValue();
void setFragmentStackChangedListener(Runnable onFragmentStackChanged);
boolean isTransitionAnimationInProgress();
void resumeDelayedFragmentAnimation();
boolean isInPassivePreviewMode();
void setInBubbleMode(boolean bubbleMode);
boolean isInBubbleMode();
boolean isInPreviewMode();
boolean isPreviewOpenAnimationInProgress();
void movePreviewFragment(float dy);
void expandPreviewFragment();
void finishPreviewFragment();
void setFragmentPanTranslationOffset(int offset);
ViewGroup getOverlayContainerView();
void setHighlightActionButtons(boolean highlight);
float getCurrentPreviewFragmentAlpha();
void drawCurrentPreviewFragment(Canvas canvas, Drawable foregroundDrawable);
void drawHeaderShadow(Canvas canvas, int alpha, int y);
boolean isSwipeInProgress();
void onPause();
void onResume();
void onBackPressed();
void onUserLeaveHint();
void onLowMemory();
boolean extendActionMode(Menu menu);
void onActionModeStarted(Object mode);
void onActionModeFinished(Object mode);
void startActivityForResult(Intent intent, int requestCode);
// TODO: Migrate them to be out of navigation layout
Theme.MessageDrawable getMessageDrawableOutStart();
Theme.MessageDrawable getMessageDrawableOutMediaStart();
// TODO: Make something like FieldsContainer and put them there?
List<BackButtonMenu.PulledDialog> getPulledDialogs();
void setPulledDialogs(List<BackButtonMenu.PulledDialog> pulledDialogs);
static INavigationLayout newLayout(Context context) {
return SharedConfig.useLNavigation ? new LNavigation(context) : new ActionBarLayout(context);
}
default boolean hasIntegratedBlurInPreview() {
return false;
}
default void rebuildFragments(int flags) {
if ((flags & REBUILD_FLAG_REBUILD_ONLY_LAST) != 0) {
showLastFragment();
return;
}
boolean last = (flags & REBUILD_FLAG_REBUILD_LAST) != 0;
rebuildAllFragmentViews(last, last);
}
default void setBackgroundView(View backgroundView) {
// Not always required
}
default void setUseAlphaAnimations(boolean useAlphaAnimations) {
// Not always required
}
/**
* @deprecated Should be replaced with {@link INavigationLayout#rebuildFragments(int)}
*/
@Deprecated
default void rebuildLogout() {
// No-op usually, can contain hackfixes
}
/**
* @deprecated Should be replaced with {@link INavigationLayout#rebuildFragments(int)}
*/
@Deprecated
default void showLastFragment() {}
/**
* @deprecated Should be replaced with {@link INavigationLayout#rebuildFragments(int)}
*/
@Deprecated
default void rebuildAllFragmentViews(boolean last, boolean showLastAfter) {}
default void drawHeaderShadow(Canvas canvas, int y) {
drawHeaderShadow(canvas, 0xFF, y);
}
default BaseFragment getBackgroundFragment() {
return getFragmentStack().size() <= 1 ? null : getFragmentStack().get(getFragmentStack().size() - 2);
}
default BaseFragment getLastFragment() {
return getFragmentStack().isEmpty() ? null : getFragmentStack().get(getFragmentStack().size() - 1);
}
default void animateThemedValues(Theme.ThemeInfo theme, int accentId, boolean nightTheme, boolean instant) {
animateThemedValues(new ThemeAnimationSettings(theme, accentId, nightTheme, instant), null);
}
default void animateThemedValues(Theme.ThemeInfo theme, int accentId, boolean nightTheme, boolean instant, Runnable onDone) {
animateThemedValues(new ThemeAnimationSettings(theme, accentId, nightTheme, instant), onDone);
}
/**
* @deprecated Deprecated in favor of {@link INavigationLayout#bringToFront(int)}
*/
@Deprecated
default void showFragment(int i) {
bringToFront(i);
}
default void bringToFront(int i) {
BaseFragment fragment = getFragmentStack().get(i);
removeFragmentFromStack(fragment);
addFragmentToStack(fragment);
rebuildFragments(REBUILD_FLAG_REBUILD_ONLY_LAST);
}
default void removeAllFragments() {
for (BaseFragment fragment : new ArrayList<>(getFragmentStack())) {
removeFragmentFromStack(fragment);
}
}
default Activity getParentActivity() {
Context ctx = getView().getContext();
if (ctx instanceof Activity) {
return (Activity) ctx;
}
throw new IllegalArgumentException("NavigationLayout added in non-activity context!");
}
default ViewGroup getView() {
if (this instanceof ViewGroup) {
return (ViewGroup) this;
}
throw new IllegalArgumentException("You should override getView() if you're not inheriting from it.");
}
default void closeLastFragment() {
closeLastFragment(true);
}
default void closeLastFragment(boolean animated) {
closeLastFragment(animated, false);
}
default void setFragmentStack(List<BaseFragment> stack) {
init(stack);
}
/**
* @deprecated This method was replaced with {@link INavigationLayout#setFragmentStack(List)}
*/
@Deprecated
default void init(List<BaseFragment> stack) {
throw new RuntimeException("Neither setFragmentStack(...) or init(...) were overriden!");
}
default void removeFragmentFromStack(int i) {
if (i < 0 || i >= getFragmentStack().size()) {
return;
}
removeFragmentFromStack(getFragmentStack().get(i));
}
default boolean addFragmentToStack(BaseFragment fragment) {
return addFragmentToStack(fragment, -1);
}
default boolean presentFragment(BaseFragment fragment) {
return presentFragment(new NavigationParams(fragment));
}
default boolean presentFragment(BaseFragment fragment, boolean removeLast) {
return presentFragment(new NavigationParams(fragment).setRemoveLast(removeLast));
}
default boolean presentFragmentAsPreview(BaseFragment fragment) {
return presentFragment(new NavigationParams(fragment).setPreview(true));
}
default boolean presentFragmentAsPreviewWithMenu(BaseFragment fragment, ActionBarPopupWindow.ActionBarPopupWindowLayout menuView) {
return presentFragment(new NavigationParams(fragment).setPreview(true).setMenuView(menuView));
}
/**
* @deprecated You should use {@link INavigationLayout.NavigationParams} for advanced params
*/
@Deprecated
default boolean presentFragment(BaseFragment fragment, boolean removeLast, boolean forceWithoutAnimation, boolean check, boolean preview) {
return presentFragment(new NavigationParams(fragment).setRemoveLast(removeLast).setNoAnimation(forceWithoutAnimation).setCheckPresentFromDelegate(check).setPreview(preview));
}
/**
* @deprecated You should use {@link INavigationLayout.NavigationParams} for advanced params
*/
@Deprecated
default boolean presentFragment(BaseFragment fragment, boolean removeLast, boolean forceWithoutAnimation, boolean check, boolean preview, ActionBarPopupWindow.ActionBarPopupWindowLayout menuView) {
return presentFragment(new NavigationParams(fragment).setRemoveLast(removeLast).setNoAnimation(forceWithoutAnimation).setCheckPresentFromDelegate(check).setPreview(preview).setMenuView(menuView));
}
default void dismissDialogs() {
List<BaseFragment> fragmentsStack = getFragmentStack();
if (!fragmentsStack.isEmpty()) {
BaseFragment lastFragment = fragmentsStack.get(fragmentsStack.size() - 1);
lastFragment.dismissCurrentDialog();
}
}
interface INavigationLayoutDelegate {
default boolean needPresentFragment(INavigationLayout layout, NavigationParams params) {
return needPresentFragment(params.fragment, params.removeLast, params.noAnimation, layout);
}
/**
* @deprecated You should override {@link INavigationLayoutDelegate#needPresentFragment(INavigationLayout, NavigationParams)} for more fields
*/
default boolean needPresentFragment(BaseFragment fragment, boolean removeLast, boolean forceWithoutAnimation, INavigationLayout layout) {
return true;
}
default boolean needAddFragmentToStack(BaseFragment fragment, INavigationLayout layout) {
return true;
}
default boolean onPreIme() {
return false;
}
default boolean needCloseLastFragment(INavigationLayout layout) {
return true;
}
default void onMeasureOverride(int[] measureSpec) {}
default void onRebuildAllFragments(INavigationLayout layout, boolean last) {}
default void onThemeProgress(float progress) {}
}
class NavigationParams {
public BaseFragment fragment;
public boolean removeLast;
public boolean noAnimation;
public boolean checkPresentFromDelegate = true;
public boolean preview;
public ActionBarPopupWindow.ActionBarPopupWindowLayout menuView;
public boolean needDelayWithoutAnimation;
public boolean isFromDelay;
public boolean delayDone;
public NavigationParams(BaseFragment fragment) {
this.fragment = fragment;
}
public NavigationParams setRemoveLast(boolean removeLast) {
this.removeLast = removeLast;
return this;
}
public NavigationParams setNoAnimation(boolean noAnimation) {
this.noAnimation = noAnimation;
return this;
}
public NavigationParams setCheckPresentFromDelegate(boolean checkPresentFromDelegate) {
this.checkPresentFromDelegate = checkPresentFromDelegate;
return this;
}
public NavigationParams setPreview(boolean preview) {
this.preview = preview;
return this;
}
public NavigationParams setMenuView(ActionBarPopupWindow.ActionBarPopupWindowLayout menuView) {
this.menuView = menuView;
return this;
}
public NavigationParams setNeedDelayWithoutAnimation(boolean needDelayWithoutAnimation) {
this.needDelayWithoutAnimation = needDelayWithoutAnimation;
return this;
}
}
class ThemeAnimationSettings {
public final Theme.ThemeInfo theme;
public final int accentId;
public final boolean nightTheme;
public final boolean instant;
public boolean onlyTopFragment;
public boolean applyTheme = true;
public Runnable afterStartDescriptionsAddedRunnable;
public Runnable beforeAnimationRunnable;
public Runnable afterAnimationRunnable;
public onAnimationProgress animationProgress;
public long duration = 200;
public Theme.ResourcesProvider resourcesProvider;
public ThemeAnimationSettings(Theme.ThemeInfo theme, int accentId, boolean nightTheme, boolean instant) {
this.theme = theme;
this.accentId = accentId;
this.nightTheme = nightTheme;
this.instant = instant;
}
public interface onAnimationProgress {
void setProgress(float p);
}
}
class StartColorsProvider implements Theme.ResourcesProvider {
HashMap<String, Integer> colors = new HashMap<>();
String[] keysToSave = new String[] {
Theme.key_chat_outBubble,
Theme.key_chat_outBubbleGradient1,
Theme.key_chat_outBubbleGradient2,
Theme.key_chat_outBubbleGradient3,
Theme.key_chat_outBubbleGradientAnimated,
Theme.key_chat_outBubbleShadow
};
@Override
public Integer getColor(String key) {
return colors.get(key);
}
@Override
public Integer getCurrentColor(String key) {
return colors.get(key);
}
public void saveColors(Theme.ResourcesProvider fragmentResourceProvider) {
colors.clear();
for (String key : keysToSave) {
colors.put(key, fragmentResourceProvider.getCurrentColor(key));
}
}
}
}

View File

@ -27,14 +27,11 @@ import android.text.SpannableStringBuilder;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
import androidx.core.math.MathUtils;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.Emoji;
import org.telegram.ui.Cells.DialogCell;
@ -71,6 +68,7 @@ public class SimpleTextView extends View implements Drawable.Callback {
private int rightDrawableTopPadding;
private boolean buildFullLayout;
private float fullAlpha;
private boolean widthWrapContent;
private Drawable wrapBackgroundDrawable;
@ -82,6 +80,7 @@ public class SimpleTextView extends View implements Drawable.Callback {
private Paint fadePaint;
private Paint fadePaintBack;
private Paint fadeEllpsizePaint;
private int fadeEllpsizePaintWidth;
private int lastWidth;
private int offsetX;
@ -95,6 +94,7 @@ public class SimpleTextView extends View implements Drawable.Callback {
private boolean rightDrawableOutside;
private boolean ellipsizeByGradient;
private int ellipsizeByGradientWidthDp = 16;
private int paddingRight;
private int minWidth;
@ -124,6 +124,8 @@ public class SimpleTextView extends View implements Drawable.Callback {
private AnimatedEmojiSpan.EmojiGroupedSpans emojiStack;
private boolean attachedToWindow;
private Layout.Alignment mAlignment = Layout.Alignment.ALIGN_NORMAL;
public SimpleTextView(Context context) {
super(context);
textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
@ -196,6 +198,16 @@ public class SimpleTextView extends View implements Drawable.Callback {
updateFadePaints();
}
public void setEllipsizeByGradient(int value) {
setEllipsizeByGradient(true);
ellipsizeByGradientWidthDp = value;
updateFadePaints();
}
public void setWidthWrapContent(boolean value) {
widthWrapContent = value;
}
private void updateFadePaints() {
if ((fadePaint == null || fadePaintBack == null) && scrollNonFitText) {
fadePaint = new Paint();
@ -206,9 +218,9 @@ public class SimpleTextView extends View implements Drawable.Callback {
fadePaintBack.setShader(new LinearGradient(0, 0, AndroidUtilities.dp(6), 0, new int[]{0, 0xffffffff}, new float[]{0f, 1f}, Shader.TileMode.CLAMP));
fadePaintBack.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
}
if (fadeEllpsizePaint == null && ellipsizeByGradient) {
if ((fadeEllpsizePaint == null || fadeEllpsizePaintWidth != AndroidUtilities.dp(ellipsizeByGradientWidthDp)) && ellipsizeByGradient) {
fadeEllpsizePaint = new Paint();
fadeEllpsizePaint.setShader(new LinearGradient(0, 0, AndroidUtilities.dp(16), 0, new int[]{0, 0xffffffff}, new float[]{0f, 1f}, Shader.TileMode.CLAMP));
fadeEllpsizePaint.setShader(new LinearGradient(0, 0, fadeEllpsizePaintWidth = AndroidUtilities.dp(ellipsizeByGradientWidthDp), 0, new int[]{0, 0xffffffff}, new float[]{0f, 1f}, Shader.TileMode.CLAMP));
fadeEllpsizePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
}
}
@ -325,7 +337,7 @@ public class SimpleTextView extends View implements Drawable.Callback {
string = TextUtils.ellipsize(string, textPaint, width, TextUtils.TruncateAt.END);
}
if (!ellipsizeByGradient && !string.equals(text)) {
fullLayout = StaticLayoutEx.createStaticLayout(text, 0, text.length(), textPaint, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false, TextUtils.TruncateAt.END, width, fullTextMaxLines, false);
fullLayout = StaticLayoutEx.createStaticLayout(text, 0, text.length(), textPaint, width, getAlignment(), 1.0f, 0.0f, false, TextUtils.TruncateAt.END, width, fullTextMaxLines, false);
if (fullLayout != null) {
int end = fullLayout.getLineEnd(0);
int start = fullLayout.getLineStart(1);
@ -338,22 +350,22 @@ public class SimpleTextView extends View implements Drawable.Callback {
} else {
part = "";
}
firstLineLayout = new StaticLayout(string, 0, string.length(), textPaint, scrollNonFitText ? AndroidUtilities.dp(2000) : width + AndroidUtilities.dp(8), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
layout = new StaticLayout(substr, 0, substr.length(), textPaint, scrollNonFitText ? AndroidUtilities.dp(2000) : width + AndroidUtilities.dp(8), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
firstLineLayout = new StaticLayout(string, 0, string.length(), textPaint, scrollNonFitText ? AndroidUtilities.dp(2000) : width + AndroidUtilities.dp(8), getAlignment(), 1.0f, 0.0f, false);
layout = new StaticLayout(substr, 0, substr.length(), textPaint, scrollNonFitText ? AndroidUtilities.dp(2000) : width + AndroidUtilities.dp(8), getAlignment(), 1.0f, 0.0f, false);
if (layout.getLineLeft(0) != 0) {
part = "\u200F" + part;
}
partLayout = new StaticLayout(part, 0, part.length(), textPaint, scrollNonFitText ? AndroidUtilities.dp(2000) : width + AndroidUtilities.dp(8), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
fullLayout = StaticLayoutEx.createStaticLayout(full, 0, full.length(), textPaint, width + AndroidUtilities.dp(8) + fullLayoutAdditionalWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false, TextUtils.TruncateAt.END, width + fullLayoutAdditionalWidth, fullTextMaxLines, false);
partLayout = new StaticLayout(part, 0, part.length(), textPaint, scrollNonFitText ? AndroidUtilities.dp(2000) : width + AndroidUtilities.dp(8), getAlignment(), 1.0f, 0.0f, false);
fullLayout = StaticLayoutEx.createStaticLayout(full, 0, full.length(), textPaint, width + AndroidUtilities.dp(8) + fullLayoutAdditionalWidth, getAlignment(), 1.0f, 0.0f, false, TextUtils.TruncateAt.END, width + fullLayoutAdditionalWidth, fullTextMaxLines, false);
}
} else {
layout = new StaticLayout(string, 0, string.length(), textPaint, scrollNonFitText || ellipsizeByGradient ? AndroidUtilities.dp(2000) : width + AndroidUtilities.dp(8), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
layout = new StaticLayout(string, 0, string.length(), textPaint, scrollNonFitText || ellipsizeByGradient ? AndroidUtilities.dp(2000) : width + AndroidUtilities.dp(8), getAlignment(), 1.0f, 0.0f, false);
fullLayout = null;
partLayout = null;
firstLineLayout = null;
}
} else if (maxLines > 1) {
layout = StaticLayoutEx.createStaticLayout(text, 0, text.length(), textPaint, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false, TextUtils.TruncateAt.END, width, maxLines, false);
layout = StaticLayoutEx.createStaticLayout(text, 0, text.length(), textPaint, width, getAlignment(), 1.0f, 0.0f, false, TextUtils.TruncateAt.END, width, maxLines, false);
} else {
CharSequence string;
if (scrollNonFitText || ellipsizeByGradient) {
@ -365,7 +377,7 @@ public class SimpleTextView extends View implements Drawable.Callback {
calcOffset(width);
return false;
}*/
layout = new StaticLayout(string, 0, string.length(), textPaint, scrollNonFitText || ellipsizeByGradient ? AndroidUtilities.dp(2000) : width + AndroidUtilities.dp(8), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
layout = new StaticLayout(string, 0, string.length(), textPaint, scrollNonFitText || ellipsizeByGradient ? AndroidUtilities.dp(2000) : width + AndroidUtilities.dp(8), getAlignment(), 1.0f, 0.0f, false);
}
spoilersPool.addAll(spoilers);
@ -390,6 +402,15 @@ public class SimpleTextView extends View implements Drawable.Callback {
return true;
}
public void setAlignment(Layout.Alignment alignment) {
mAlignment = alignment;
requestLayout();
}
private Layout.Alignment getAlignment() {
return mAlignment;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
@ -407,6 +428,10 @@ public class SimpleTextView extends View implements Drawable.Callback {
} else {
finalHeight = getPaddingTop() + textHeight + getPaddingBottom();
}
if (widthWrapContent) {
// textWidth = (int) Math.ceil(layout.getLineWidth(0));
width = Math.min(width, getPaddingLeft() + textWidth + getPaddingRight() + minusWidth + (rightDrawableOutside && rightDrawable != null ? rightDrawable.getIntrinsicWidth() + drawablePadding : 0));
}
setMeasuredDimension(width, finalHeight);
if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.CENTER_VERTICAL) {
@ -632,7 +657,10 @@ public class SimpleTextView extends View implements Drawable.Callback {
}
public void setRightPadding(int padding) {
paddingRight = padding;
if (paddingRight != padding) {
paddingRight = padding;
invalidate();
}
}
@Override
@ -803,10 +831,10 @@ public class SimpleTextView extends View implements Drawable.Callback {
canvas.translate(getMaxTextWidth() - paddingRight - AndroidUtilities.dp(6), 0);
canvas.drawRect(0, 0, AndroidUtilities.dp(6), getMeasuredHeight(), fadePaintBack);
canvas.restore();
} else if (ellipsizeByGradient && fadeEllpsizePaint != null) {
} else if (ellipsizeByGradient && (!widthWrapContent || textDoesNotFit) && fadeEllpsizePaint != null) {
canvas.save();
canvas.translate(getMaxTextWidth() - paddingRight - AndroidUtilities.dp(rightDrawable != null && !(rightDrawable instanceof AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable) && rightDrawableOutside ? 18 : 16), 0);
canvas.drawRect(0, 0, AndroidUtilities.dp(16), getMeasuredHeight(), fadeEllpsizePaint);
canvas.translate(getMaxTextWidth() - paddingRight - fadeEllpsizePaintWidth - AndroidUtilities.dp(rightDrawable != null && !(rightDrawable instanceof AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable) && rightDrawableOutside ? +2 : 0), 0);
canvas.drawRect(0, 0, fadeEllpsizePaintWidth, getMeasuredHeight(), fadeEllpsizePaint);
canvas.restore();
}
updateScrollAnimation();

View File

@ -170,6 +170,10 @@ public class Theme {
public boolean isSelected;
private Path path;
public Path getPath() {
return path;
}
private Rect backupRect = new Rect();
private final ResourcesProvider resourcesProvider;
@ -183,6 +187,7 @@ public class Theme {
public static MotionBackgroundDrawable[] motionBackground = new MotionBackgroundDrawable[3];
private int[] currentShadowDrawableRadius = new int[]{-1, -1, -1, -1};
private Bitmap[] shadowDrawableBitmap = new Bitmap[4];
private Drawable[] shadowDrawable = new Drawable[4];
private int[] shadowDrawableColor = new int[]{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
@ -212,6 +217,8 @@ public class Theme {
PathDrawParams pathDrawCacheParams;
private int overrideRoundRadius;
private float overrideRounding;
public boolean forceInvalidatePath;
public MessageDrawable(int type, boolean out, boolean selected) {
this(type, out, selected, null);
@ -430,7 +437,14 @@ public class Theme {
}
public Drawable getBackgroundDrawable() {
int newRad = AndroidUtilities.dp(SharedConfig.bubbleRadius);
int newRad;
if (overrideRoundRadius != 0) {
newRad = overrideRoundRadius;
} else if (overrideRounding > 0) {
newRad = 0;
} else {
newRad = AndroidUtilities.dp(SharedConfig.bubbleRadius);
}
int idx;
if (isTopNear && isBottomNear) {
idx = 3;
@ -557,6 +571,9 @@ public class Theme {
boolean forceSetColor = false;
if (currentShadowDrawableRadius[idx] != newRad) {
currentShadowDrawableRadius[idx] = newRad;
if (shadowDrawableBitmap[idx] != null) {
shadowDrawableBitmap[idx].recycle();
}
try {
Bitmap bitmap = Bitmap.createBitmap(dp(50), dp(40), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
@ -582,6 +599,7 @@ public class Theme {
draw(canvas, shadowPaint);
}
shadowDrawableBitmap[idx] = bitmap;
shadowDrawable[idx] = new NinePatchDrawable(bitmap, getByteBuffer(bitmap.getWidth() / 2 - 1, bitmap.getWidth() / 2 + 1, bitmap.getHeight() / 2 - 1, bitmap.getHeight() / 2 + 1).array(), new Rect(), null);
forceSetColor = true;
} catch (Throwable ignore) {
@ -596,6 +614,20 @@ public class Theme {
return shadowDrawable[idx];
}
@Override
protected void finalize() throws Throwable {
super.finalize();
for (Bitmap bitmap : shadowDrawableBitmap) {
if (bitmap != null) {
bitmap.recycle();
}
}
Arrays.fill(shadowDrawableBitmap, null);
Arrays.fill(shadowDrawable, null);
Arrays.fill(currentShadowDrawableRadius, -1);
}
private static ByteBuffer getByteBuffer(int x1, int x2, int y1, int y2) {
ByteBuffer buffer = ByteBuffer.allocate(4 + 4 * 7 + 4 * 2 + 4 * 2 + 4 * 9).order(ByteOrder.nativeOrder());
buffer.put((byte) 0x01);
@ -662,7 +694,7 @@ public class Theme {
public void draw(Canvas canvas, Paint paintToUse) {
Rect bounds = getBounds();
if (paintToUse == null && gradientShader == null) {
if (paintToUse == null && gradientShader == null && overrideRoundRadius == 0 && overrideRounding <= 0) {
Drawable background = getBackgroundDrawable();
if (background != null) {
background.setBounds(bounds);
@ -676,12 +708,15 @@ public class Theme {
if (overrideRoundRadius != 0) {
rad = overrideRoundRadius;
nearRad = overrideRoundRadius;
} else if (overrideRounding > 0) {
rad = AndroidUtilities.lerp(dp(SharedConfig.bubbleRadius), Math.min(bounds.width(), bounds.height()) / 2, overrideRounding);
nearRad = AndroidUtilities.lerp(dp(Math.min(6, SharedConfig.bubbleRadius)), Math.min(bounds.width(), bounds.height()) / 2, overrideRounding);
} else if (currentType == TYPE_PREVIEW) {
rad = dp(6);
nearRad = dp(6);
} else {
rad = dp(SharedConfig.bubbleRadius);
nearRad = dp(Math.min(5, SharedConfig.bubbleRadius));
nearRad = dp(Math.min(6, SharedConfig.bubbleRadius));
}
int smallRad = dp(6);
@ -712,8 +747,8 @@ public class Theme {
path = this.path;
invalidatePath = true;
}
if (invalidatePath) {
path.reset();
if (invalidatePath || overrideRoundRadius != 0) {
path.rewind();
if (isOut) {
if (drawFullBubble || currentType == TYPE_PREVIEW || paintToUse != null || drawFullBottom) {
if (currentType == TYPE_MEDIA) {
@ -888,6 +923,10 @@ public class Theme {
this.overrideRoundRadius = radius;
}
public void setRoundingRadius(float rounding) {
this.overrideRounding = rounding;
}
public static class PathDrawParams {
Path path = new Path();
Rect lastRect = new Rect();
@ -901,6 +940,10 @@ public class Theme {
lastRect.set(bounds);
return invalidate;
}
public Path getPath() {
return path;
}
}
}
@ -2366,6 +2409,21 @@ public class Theme {
themeAccent.backgroundRotation = patternRotations[a];
themeAccent.patternSlug = patternSlugs[a];
}
//override default themes
if (isHome(themeAccent) && name.equals("Dark Blue") || name.equals("Night")) {
themeAccent.myMessagesAccentColor = 0xff6573f8;
themeAccent.myMessagesGradientAccentColor1 = 0xff7644cb;
themeAccent.myMessagesGradientAccentColor2 = 0xff8849b4;
themeAccent.myMessagesGradientAccentColor3 = 0xffa751a8;
if (name.equals("Night")) {
themeAccent.patternIntensity = -0.57f;
themeAccent.backgroundOverrideColor = 0xff6c7fa6;
themeAccent.backgroundGradientOverrideColor1 = 0xff2e344b;
themeAccent.backgroundGradientOverrideColor2 = 0xff7874a7;
themeAccent.backgroundGradientOverrideColor3 = 0xff333258;
}
}
themeAccentsMap.put(themeAccent.id, themeAccent);
themeAccents.add(themeAccent);
}
@ -2863,7 +2921,9 @@ public class Theme {
public static Drawable dialogs_errorDrawable;
public static Drawable dialogs_reorderDrawable;
public static Drawable dialogs_lockDrawable;
public static Drawable dialogs_lock2Drawable;
public static Drawable dialogs_muteDrawable;
public static Drawable dialogs_unmuteDrawable;
public static Drawable dialogs_verifiedDrawable;
public static ScamDrawable dialogs_scamDrawable;
public static ScamDrawable dialogs_fakeDrawable;
@ -2954,6 +3014,7 @@ public class Theme {
public static TextPaint chat_forwardNamePaint;
public static TextPaint chat_replyNamePaint;
public static TextPaint chat_replyTextPaint;
public static TextPaint chat_commentTextPaint;
public static TextPaint chat_contextResult_titleTextPaint;
public static TextPaint chat_contextResult_descriptionTextPaint;
@ -3895,6 +3956,7 @@ public class Theme {
public static final String key_drawable_goIcon = "drawableGoIcon";
public static final String key_drawable_msgError = "drawableMsgError";
public static final String key_drawable_msgIn = "drawableMsgIn";
public static final String key_drawable_msgInInstant = "drawableMsgInInstant";
public static final String key_drawable_msgInClock = "drawableMsgInClock";
public static final String key_drawable_msgInClockSelected = "drawableMsgInClockSelected";
public static final String key_drawable_msgInSelected = "drawableMsgInSelected";
@ -6538,13 +6600,33 @@ public class Theme {
for (int a = 0; a < count; a++) {
Drawable layer = drawable.getDrawable(a);
if (layer instanceof RippleRadMaskDrawable) {
drawable.setDrawableByLayerId(android.R.id.mask, new RippleRadMaskDrawable(top, bottom));
((RippleRadMaskDrawable) layer).setRadius(top, bottom);
// drawable.setDrawableByLayerId(android.R.id.mask, new RippleRadMaskDrawable(top, bottom));
break;
}
}
}
}
public static void setMaskDrawableRad(Drawable rippleDrawable, int topLeftRad, int topRightRad, int bottomRightRad, int bottomLeftRad) {
if (Build.VERSION.SDK_INT < 21) {
return;
}
if (rippleDrawable instanceof RippleDrawable) {
RippleDrawable drawable = (RippleDrawable) rippleDrawable;
int count = drawable.getNumberOfLayers();
for (int a = 0; a < count; a++) {
Drawable layer = drawable.getDrawable(a);
if (layer instanceof RippleRadMaskDrawable) {
((RippleRadMaskDrawable) layer).setRadius(topLeftRad, topRightRad, bottomRightRad, bottomLeftRad);
// drawable.setDrawableByLayerId(android.R.id.mask, new RippleRadMaskDrawable(top, bottom));
break;
}
}
}
}
public static Drawable createRadSelectorDrawable(int color, int topRad, int bottomRad) {
if (Build.VERSION.SDK_INT >= 21) {
maskPaint.setColor(0xffffffff);
@ -7660,7 +7742,7 @@ public class Theme {
switchingNightTheme = false;
}
} else {
if (currentTheme != currentDayTheme && (currentTheme == null || currentNightTheme != null && currentTheme.isDark() != currentNightTheme.isDark())) {
if (currentTheme != currentDayTheme && (currentTheme == null || currentDayTheme != null && currentTheme.isLight() != currentDayTheme.isLight())) {
isInNigthMode = false;
lastThemeSwitchTime = SystemClock.elapsedRealtime();
switchingNightTheme = true;
@ -8796,6 +8878,7 @@ public class Theme {
dialogs_actionMessagePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
dialogs_lockDrawable = resources.getDrawable(R.drawable.list_secret);
dialogs_lock2Drawable = resources.getDrawable(R.drawable.msg_mini_lock2);
dialogs_checkDrawable = resources.getDrawable(R.drawable.list_check).mutate();
dialogs_playDrawable = resources.getDrawable(R.drawable.minithumb_play).mutate();
dialogs_checkReadDrawable = resources.getDrawable(R.drawable.list_check).mutate();
@ -8804,6 +8887,7 @@ public class Theme {
dialogs_errorDrawable = resources.getDrawable(R.drawable.list_warning_sign);
dialogs_reorderDrawable = resources.getDrawable(R.drawable.list_reorder).mutate();
dialogs_muteDrawable = resources.getDrawable(R.drawable.list_mute).mutate();
dialogs_unmuteDrawable = resources.getDrawable(R.drawable.list_unmute).mutate();
dialogs_verifiedDrawable = resources.getDrawable(R.drawable.verified_area).mutate();
dialogs_scamDrawable = new ScamDrawable(11, 0);
dialogs_fakeDrawable = new ScamDrawable(11, 1);
@ -8873,6 +8957,7 @@ public class Theme {
dialogs_offlinePaint.setColor(getColor(key_windowBackgroundWhiteGrayText3));
setDrawableColorByKey(dialogs_lockDrawable, key_chats_secretIcon);
setDrawableColorByKey(dialogs_lock2Drawable, key_chats_pinnedIcon);
setDrawableColorByKey(dialogs_checkDrawable, key_chats_sentCheck);
setDrawableColorByKey(dialogs_checkReadDrawable, key_chats_sentReadCheck);
setDrawableColorByKey(dialogs_halfCheckDrawable, key_chats_sentReadCheck);
@ -8881,6 +8966,7 @@ public class Theme {
setDrawableColorByKey(dialogs_pinnedDrawable, key_chats_pinnedIcon);
setDrawableColorByKey(dialogs_reorderDrawable, key_chats_pinnedIcon);
setDrawableColorByKey(dialogs_muteDrawable, key_chats_muteIcon);
setDrawableColorByKey(dialogs_unmuteDrawable, key_chats_muteIcon);
setDrawableColorByKey(dialogs_mentionDrawable, key_chats_mentionIcon);
setDrawableColorByKey(dialogs_reactionsMentionDrawable, key_chats_mentionIcon);
setDrawableColorByKey(dialogs_verifiedDrawable, key_chats_verifiedBackground);
@ -8922,6 +9008,14 @@ public class Theme {
chat_msgTextPaintThreeEmoji = new TextPaint(Paint.ANTI_ALIAS_FLAG);
chat_msgBotButtonPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
chat_msgBotButtonPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
chat_namePaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
chat_namePaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
chat_replyNamePaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
chat_replyNamePaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
chat_replyTextPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
chat_forwardNamePaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
chat_adminPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
chat_timePaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
}
final float[] emojiSizePercents = new float[] {.7f, .52f, .37f, .28f, .25f, .19f};
@ -8935,6 +9029,14 @@ public class Theme {
chat_msgTextPaint.setTextSize(AndroidUtilities.dp(SharedConfig.fontSize));
chat_msgGameTextPaint.setTextSize(AndroidUtilities.dp(14));
chat_msgBotButtonPaint.setTextSize(AndroidUtilities.dp(15));
float smallerDp = (2 * SharedConfig.fontSize + 10) / 3f; // 6f + SharedConfig.fontSize / 2f;
chat_namePaint.setTextSize(AndroidUtilities.dp(smallerDp));
chat_replyNamePaint.setTextSize(AndroidUtilities.dp(smallerDp));
chat_replyTextPaint.setTextSize(AndroidUtilities.dp(smallerDp));
chat_forwardNamePaint.setTextSize(AndroidUtilities.dp(smallerDp));
chat_adminPaint.setTextSize(AndroidUtilities.dp(smallerDp - 1));
// float timeDp = 2 * (SharedConfig.fontSize - 16) / 3f + 12;
// chat_timePaint.setTextSize(AndroidUtilities.dp(timeDp));
}
}
@ -8991,6 +9093,7 @@ public class Theme {
chat_replyNamePaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
chat_replyNamePaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
chat_replyTextPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
chat_commentTextPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
chat_instantViewPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
chat_instantViewPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
chat_instantViewRectPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@ -9272,6 +9375,7 @@ public class Theme {
addChatDrawable(key_drawable_msgInSelected, chat_msgInSelectedDrawable, null);
addChatDrawable(key_drawable_msgInMedia, chat_msgInMediaDrawable, null);
addChatDrawable(key_drawable_msgInMediaSelected, chat_msgInMediaSelectedDrawable, null);
addChatDrawable(key_drawable_msgInInstant, chat_msgInInstantDrawable, key_chat_inInstant);
addChatDrawable(key_drawable_msgOut, chat_msgOutDrawable, null);
addChatDrawable(key_drawable_msgOutSelected, chat_msgOutSelectedDrawable, null);
addChatDrawable(key_drawable_msgOutMedia, chat_msgOutMediaDrawable, null);
@ -9325,12 +9429,14 @@ public class Theme {
chat_contactNamePaint.setTextSize(AndroidUtilities.dp(15));
chat_contactPhonePaint.setTextSize(AndroidUtilities.dp(13));
chat_durationPaint.setTextSize(AndroidUtilities.dp(12));
float smallerDp = (2 * SharedConfig.fontSize + 10) / 3f; // 6f + SharedConfig.fontSize / 2f;
chat_namePaint.setTextSize(AndroidUtilities.dp(smallerDp));
chat_replyNamePaint.setTextSize(AndroidUtilities.dp(smallerDp));
chat_replyTextPaint.setTextSize(AndroidUtilities.dp(smallerDp));
chat_forwardNamePaint.setTextSize(AndroidUtilities.dp(smallerDp));
chat_adminPaint.setTextSize(AndroidUtilities.dp(smallerDp - 1));
float timeDp = 2 * (SharedConfig.fontSize - 16) / 3f + 12;
chat_timePaint.setTextSize(AndroidUtilities.dp(12));
chat_adminPaint.setTextSize(AndroidUtilities.dp(13));
chat_namePaint.setTextSize(AndroidUtilities.dp(14));
chat_forwardNamePaint.setTextSize(AndroidUtilities.dp(14));
chat_replyNamePaint.setTextSize(AndroidUtilities.dp(14));
chat_replyTextPaint.setTextSize(AndroidUtilities.dp(14));
chat_gamePaint.setTextSize(AndroidUtilities.dp(13));
chat_shipmentPaint.setTextSize(AndroidUtilities.dp(13));
chat_instantViewPaint.setTextSize(AndroidUtilities.dp(13));
@ -9342,6 +9448,8 @@ public class Theme {
chat_contextResult_descriptionTextPaint.setTextSize(AndroidUtilities.dp(13));
chat_radialProgressPaint.setStrokeWidth(AndroidUtilities.dp(3));
chat_radialProgress2Paint.setStrokeWidth(AndroidUtilities.dp(2));
chat_commentTextPaint.setTextSize(AndroidUtilities.dp(14));
chat_commentTextPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
}
}

View File

@ -195,14 +195,14 @@ public class ActionIntroActivity extends BaseFragment implements LocationControl
if (width > height) {
titleTextView.measure(MeasureSpec.makeMeasureSpec((int) (width * 0.6f), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.UNSPECIFIED));
descriptionText.measure(MeasureSpec.makeMeasureSpec((int) (width * 0.6f), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.UNSPECIFIED));
buttonTextView.measure(MeasureSpec.makeMeasureSpec((int) (width * 0.6f), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(42), MeasureSpec.EXACTLY));
buttonTextView.measure(MeasureSpec.makeMeasureSpec((int) (width * 0.6f), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(42), MeasureSpec.EXACTLY));
} else {
titleTextView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.UNSPECIFIED));
descriptionText.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.UNSPECIFIED));
if (currentType == ACTION_TYPE_SET_PASSCODE) {
buttonTextView.measure(MeasureSpec.makeMeasureSpec(width - AndroidUtilities.dp(24 * 2), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(50), MeasureSpec.EXACTLY));
} else {
buttonTextView.measure(MeasureSpec.makeMeasureSpec((int) (width * 0.6f), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(42), MeasureSpec.EXACTLY));
buttonTextView.measure(MeasureSpec.makeMeasureSpec(width - AndroidUtilities.dp(72), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(50), MeasureSpec.EXACTLY));
}
}
break;
@ -384,7 +384,7 @@ public class ActionIntroActivity extends BaseFragment implements LocationControl
y = (int) (height * 0.493f);
descriptionText.layout(0, y, descriptionText.getMeasuredWidth(), y + descriptionText.getMeasuredHeight());
x = (width - buttonTextView.getMeasuredWidth()) / 2;
y = (int) (height * 0.71f);
y = (int) (height * 0.853f);
buttonTextView.layout(x, y, x + buttonTextView.getMeasuredWidth(), y + buttonTextView.getMeasuredHeight());
}
break;
@ -703,6 +703,7 @@ public class ActionIntroActivity extends BaseFragment implements LocationControl
titleTextView.setText(LocaleController.getString("PeopleNearby", R.string.PeopleNearby));
descriptionText.setText(LocaleController.getString("PeopleNearbyAccessInfo", R.string.PeopleNearbyAccessInfo));
buttonTextView.setText(LocaleController.getString("PeopleNearbyAllowAccess", R.string.PeopleNearbyAllowAccess));
flickerButton = true;
break;
}
case ACTION_TYPE_NEARBY_LOCATION_ENABLED: {

View File

@ -607,7 +607,7 @@ public class DialogsAdapter extends RecyclerListView.SelectionAdapter {
if (chat.participants_count != 0) {
subtitle = LocaleController.formatPluralStringComma("Subscribers", chat.participants_count);
} else {
if (TextUtils.isEmpty(chat.username)) {
if (!ChatObject.isPublic(chat)) {
subtitle = LocaleController.getString("ChannelPrivate", R.string.ChannelPrivate).toLowerCase();
} else {
subtitle = LocaleController.getString("ChannelPublic", R.string.ChannelPublic).toLowerCase();
@ -619,7 +619,7 @@ public class DialogsAdapter extends RecyclerListView.SelectionAdapter {
} else {
if (chat.has_geo) {
subtitle = LocaleController.getString("MegaLocation", R.string.MegaLocation);
} else if (TextUtils.isEmpty(chat.username)) {
} else if (!ChatObject.isPublic(chat)) {
subtitle = LocaleController.getString("MegaPrivate", R.string.MegaPrivate).toLowerCase();
} else {
subtitle = LocaleController.getString("MegaPublic", R.string.MegaPublic).toLowerCase();

View File

@ -13,11 +13,15 @@ import android.os.SystemClock;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import androidx.collection.LongSparseArray;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.telegram.PhoneFormat.PhoneFormat;
import org.telegram.SQLite.SQLiteCursor;
import org.telegram.SQLite.SQLitePreparedStatement;
@ -25,21 +29,20 @@ import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.ChatObject;
import org.telegram.messenger.ContactsController;
import org.telegram.messenger.DialogObject;
import org.telegram.messenger.MediaDataController;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.MediaDataController;
import org.telegram.messenger.MessageObject;
import org.telegram.messenger.MessagesController;
import org.telegram.messenger.MessagesStorage;
import org.telegram.messenger.R;
import org.telegram.messenger.UserConfig;
import org.telegram.messenger.UserObject;
import org.telegram.messenger.Utilities;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.R;
import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.ActionBar.ThemeDescription;
import org.telegram.ui.Cells.DialogCell;
import org.telegram.ui.Cells.GraySectionCell;
import org.telegram.ui.Cells.HashtagSearchCell;
@ -56,11 +59,6 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import androidx.collection.LongSparseArray;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
private final int VIEW_TYPE_PROFILE_CELL = 0;
@ -1124,7 +1122,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
if (chat == null) {
chat = (TLRPC.Chat) obj;
}
un = chat.username;
un = ChatObject.getPublicUsername(chat);
} else if (obj instanceof TLRPC.EncryptedChat) {
encryptedChat = MessagesController.getInstance(currentAccount).getEncryptedChat(((TLRPC.EncryptedChat) obj).id);
user = MessagesController.getInstance(currentAccount).getUser(encryptedChat.user_id);
@ -1219,9 +1217,9 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
if (chat != null && chat.participants_count != 0) {
String membersString;
if (ChatObject.isChannel(chat) && !chat.megagroup) {
membersString = LocaleController.formatPluralString("Subscribers", chat.participants_count);
membersString = LocaleController.formatPluralStringComma("Subscribers", chat.participants_count, ' ');
} else {
membersString = LocaleController.formatPluralString("Members", chat.participants_count);
membersString = LocaleController.formatPluralStringComma("Members", chat.participants_count, ' ');
}
if (username instanceof SpannableStringBuilder) {
((SpannableStringBuilder) username).append(", ").append(membersString);
@ -1381,7 +1379,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
cell.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite));
cell.useSeparator = (position != getItemCount() - 1);
MessageObject messageObject = (MessageObject) getItem(position);
cell.setDialog(messageObject.getDialogId(), messageObject, messageObject.messageOwner.date, false);
cell.setDialog(messageObject.getDialogId(), messageObject, messageObject.messageOwner.date, false, false);
break;
}
case VIEW_TYPE_HASHTAG_CELL: {

View File

@ -1113,7 +1113,7 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter implement
}
firstName = chat.title;
lastName = null;
username = chat.username;
username = ChatObject.getPublicUsername(chat);
object = chat;
id = -chat.id;
} else {

View File

@ -12,6 +12,8 @@ import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import org.telegram.messenger.MediaDataController;
import org.telegram.messenger.MessageObject;
import org.telegram.messenger.UserConfig;
@ -22,8 +24,6 @@ import org.telegram.ui.Components.RecyclerListView;
import java.util.ArrayList;
import androidx.recyclerview.widget.RecyclerView;
public class MessagesSearchAdapter extends RecyclerListView.SelectionAdapter {
private Context mContext;
@ -86,7 +86,7 @@ public class MessagesSearchAdapter extends RecyclerListView.SelectionAdapter {
DialogCell cell = (DialogCell) holder.itemView;
cell.useSeparator = true;
MessageObject messageObject = (MessageObject) getItem(position);
cell.setDialog(messageObject.getDialogId(), messageObject, messageObject.messageOwner.date, true);
cell.setDialog(messageObject.getDialogId(), messageObject, messageObject.messageOwner.date, true, false);
}
}

View File

@ -17,6 +17,7 @@ import android.view.ViewGroup;
import org.telegram.PhoneFormat.PhoneFormat;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.ChatObject;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.R;
import org.telegram.messenger.UserObject;
@ -320,7 +321,7 @@ public class SearchAdapter extends RecyclerListView.SelectionAdapter {
id = ((TLRPC.User) object).id;
self = ((TLRPC.User) object).self;
} else if (object instanceof TLRPC.Chat) {
un = ((TLRPC.Chat) object).username;
un = ChatObject.getPublicUsername((TLRPC.Chat) object);
id = ((TLRPC.Chat) object).id;
}

View File

@ -236,12 +236,12 @@ public class ArchivedStickersActivity extends BaseFragment implements Notificati
}
@Override
protected void onTransitionAnimationStart(boolean isOpen, boolean backward) {
public void onTransitionAnimationStart(boolean isOpen, boolean backward) {
isInTransition = true;
}
@Override
protected void onTransitionAnimationEnd(boolean isOpen, boolean backward) {
public void onTransitionAnimationEnd(boolean isOpen, boolean backward) {
isInTransition = false;
if (doOnTransitionEnd != null) {
doOnTransitionEnd.run();

View File

@ -104,6 +104,7 @@ import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import org.json.JSONObject;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.ApplicationLoader;
import org.telegram.messenger.ChatObject;
import org.telegram.messenger.DownloadController;
import org.telegram.messenger.FileLoader;
import org.telegram.messenger.FileLog;
@ -3201,7 +3202,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg
}
if (pageBlock instanceof TLRPC.TL_pageBlockChannel) {
TLRPC.TL_pageBlockChannel pageBlockChannel = (TLRPC.TL_pageBlockChannel) pageBlock;
MessagesController.getInstance(currentAccount).openByUserName(pageBlockChannel.channel.username, parentFragment, 2);
MessagesController.getInstance(currentAccount).openByUserName(ChatObject.getPublicUsername(pageBlockChannel.channel), parentFragment, 2);
close(false, true);
} else if (pageBlock instanceof TL_pageBlockRelatedArticlesChild) {
TL_pageBlockRelatedArticlesChild pageBlockRelatedArticlesChild = (TL_pageBlockRelatedArticlesChild) pageBlock;
@ -4161,7 +4162,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg
messageObject.messageOwner.media.webpage = webPage;
TLRPC.TL_messages_messages messagesRes = new TLRPC.TL_messages_messages();
messagesRes.messages.add(messageObject.messageOwner);
MessagesStorage.getInstance(currentAccount).putMessages(messagesRes, messageObject.getDialogId(), -2, 0, false, messageObject.scheduled);
MessagesStorage.getInstance(currentAccount).putMessages(messagesRes, messageObject.getDialogId(), -2, 0, false, messageObject.scheduled, 0);
}
pagesStack.set(0, webPage);
if (pagesStack.size() == 1) {
@ -4194,7 +4195,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg
if (messageObject != null) {
TLRPC.TL_messages_messages messagesRes = new TLRPC.TL_messages_messages();
messagesRes.messages.add(messageObject.messageOwner);
MessagesStorage.getInstance(currentAccount).putMessages(messagesRes, messageObject.getDialogId(), -2, 0, false, messageObject.scheduled);
MessagesStorage.getInstance(currentAccount).putMessages(messagesRes, messageObject.getDialogId(), -2, 0, false, messageObject.scheduled, 0);
}
}
}
@ -4652,7 +4653,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg
}
private void loadChannel(final BlockChannelCell cell, WebpageAdapter adapter, TLRPC.Chat channel) {
if (loadingChannel || TextUtils.isEmpty(channel.username)) {
if (loadingChannel || !ChatObject.isPublic(channel)) {
return;
}
loadingChannel = true;
@ -6025,7 +6026,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg
float y = event.getY();
if (channelCell.getVisibility() == VISIBLE && y > channelCell.getTranslationY() && y < channelCell.getTranslationY() + AndroidUtilities.dp(39)) {
if (parentAdapter.channelBlock != null && event.getAction() == MotionEvent.ACTION_UP) {
MessagesController.getInstance(currentAccount).openByUserName(parentAdapter.channelBlock.channel.username, parentFragment, 2);
MessagesController.getInstance(currentAccount).openByUserName(ChatObject.getPublicUsername(parentAdapter.channelBlock.channel), parentFragment, 2);
close(false, true);
}
return true;
@ -9909,7 +9910,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg
float y = event.getY();
if (channelCell.getVisibility() == VISIBLE && y > channelCell.getTranslationY() && y < channelCell.getTranslationY() + AndroidUtilities.dp(39)) {
if (parentAdapter.channelBlock != null && event.getAction() == MotionEvent.ACTION_UP) {
MessagesController.getInstance(currentAccount).openByUserName(parentAdapter.channelBlock.channel.username, parentFragment, 2);
MessagesController.getInstance(currentAccount).openByUserName(ChatObject.getPublicUsername(parentAdapter.channelBlock.channel), parentFragment, 2);
close(false, true);
}
return true;
@ -11212,6 +11213,9 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg
}
public boolean openPhoto(TLRPC.PageBlock block, WebpageAdapter adapter) {
if (parentFragment == null || parentFragment.getParentActivity() == null) {
return false;
}
final int index;
final List<TLRPC.PageBlock> pageBlocks;
if (!(block instanceof TLRPC.TL_pageBlockVideo) || WebPageUtils.isVideo(adapter.currentPage, block)) {

View File

@ -8,11 +8,7 @@
package org.telegram.ui;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock;
import android.view.View;
@ -25,19 +21,14 @@ import org.telegram.messenger.AccountInstance;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.ApplicationLoader;
import org.telegram.messenger.BuildVars;
import org.telegram.messenger.ContactsController;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.ImageLoader;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.NotificationCenter;
import org.telegram.messenger.R;
import org.telegram.messenger.SharedConfig;
import org.telegram.messenger.UserConfig;
import org.telegram.messenger.camera.CameraController;
import org.telegram.ui.ActionBar.ActionBarLayout;
import org.telegram.ui.ActionBar.AlertDialog;
import org.telegram.ui.ActionBar.BaseFragment;
import org.telegram.ui.ActionBar.DrawerLayoutContainer;
import org.telegram.ui.ActionBar.INavigationLayout;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Components.LayoutHelper;
import org.telegram.ui.Components.PasscodeView;
@ -45,13 +36,13 @@ import org.telegram.ui.Components.ThemeEditorView;
import java.util.ArrayList;
public class BubbleActivity extends BasePermissionsActivity implements ActionBarLayout.ActionBarLayoutDelegate {
public class BubbleActivity extends BasePermissionsActivity implements INavigationLayout.INavigationLayoutDelegate {
private boolean finished;
private ArrayList<BaseFragment> mainFragmentsStack = new ArrayList<>();
private PasscodeView passcodeView;
private ActionBarLayout actionBarLayout;
private INavigationLayout actionBarLayout;
protected DrawerLayoutContainer drawerLayoutContainer;
private Intent passcodeSaveIntent;
@ -89,7 +80,7 @@ public class BubbleActivity extends BasePermissionsActivity implements ActionBar
Theme.createDialogsResources(this);
Theme.createChatResources(this, false);
actionBarLayout = new ActionBarLayout(this);
actionBarLayout = INavigationLayout.newLayout(this);
actionBarLayout.setInBubbleMode(true);
actionBarLayout.setRemoveActionBarExtraHeight(true);
@ -99,11 +90,11 @@ public class BubbleActivity extends BasePermissionsActivity implements ActionBar
RelativeLayout launchLayout = new RelativeLayout(this);
drawerLayoutContainer.addView(launchLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
launchLayout.addView(actionBarLayout, LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
launchLayout.addView(actionBarLayout.getView(), LayoutHelper.createRelative(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
drawerLayoutContainer.setParentActionBarLayout(actionBarLayout);
actionBarLayout.setDrawerLayoutContainer(drawerLayoutContainer);
actionBarLayout.init(mainFragmentsStack);
actionBarLayout.setFragmentStack(mainFragmentsStack);
actionBarLayout.setDelegate(this);
passcodeView = new PasscodeView(this);
@ -188,11 +179,6 @@ public class BubbleActivity extends BasePermissionsActivity implements ActionBar
return true;
}
@Override
public boolean onPreIme() {
return false;
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
@ -246,8 +232,8 @@ public class BubbleActivity extends BasePermissionsActivity implements ActionBar
if (editorView != null) {
editorView.onActivityResult(requestCode, resultCode, data);
}
if (actionBarLayout.fragmentsStack.size() != 0) {
BaseFragment fragment = actionBarLayout.fragmentsStack.get(actionBarLayout.fragmentsStack.size() - 1);
if (actionBarLayout.getFragmentStack().size() != 0) {
BaseFragment fragment = actionBarLayout.getFragmentStack().get(actionBarLayout.getFragmentStack().size() - 1);
fragment.onActivityResultFragment(requestCode, resultCode, data);
}
}
@ -257,8 +243,8 @@ public class BubbleActivity extends BasePermissionsActivity implements ActionBar
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (!checkPermissionsResult(requestCode, permissions, grantResults)) return;
if (actionBarLayout.fragmentsStack.size() != 0) {
BaseFragment fragment = actionBarLayout.fragmentsStack.get(actionBarLayout.fragmentsStack.size() - 1);
if (actionBarLayout.getFragmentStack().size() != 0) {
BaseFragment fragment = actionBarLayout.getFragmentStack().get(actionBarLayout.getFragmentStack().size() - 1);
fragment.onRequestPermissionsResultFragment(requestCode, permissions, grantResults);
}
@ -361,27 +347,12 @@ public class BubbleActivity extends BasePermissionsActivity implements ActionBar
}
@Override
public boolean needPresentFragment(BaseFragment fragment, boolean removeLast, boolean forceWithoutAnimation, ActionBarLayout layout) {
return true;
}
@Override
public boolean needAddFragmentToStack(BaseFragment fragment, ActionBarLayout layout) {
return true;
}
@Override
public boolean needCloseLastFragment(ActionBarLayout layout) {
if (layout.fragmentsStack.size() <= 1) {
public boolean needCloseLastFragment(INavigationLayout layout) {
if (layout.getFragmentStack().size() <= 1) {
onFinish();
finish();
return false;
}
return true;
}
@Override
public void onRebuildAllFragments(ActionBarLayout layout, boolean last) {
}
}

View File

@ -110,6 +110,8 @@ public class CacheControlActivity extends BaseFragment implements NotificationCe
long fragmentCreateTime;
private boolean updateDatabaseSize;
@Override
public boolean onFragmentCreate() {
super.onFragmentCreate();
@ -537,7 +539,7 @@ public class CacheControlActivity extends BaseFragment implements NotificationCe
CheckBoxCell cell = (CheckBoxCell) v;
int num = (Integer) cell.getTag();
if (enabledCount == 1 && clearViewData[num].clear) {
AndroidUtilities.shakeView(((CheckBoxCell) v).getCheckBoxView(), 2, 0);
AndroidUtilities.shakeView(((CheckBoxCell) v).getCheckBoxView());
return;
}
@ -630,6 +632,7 @@ public class CacheControlActivity extends BaseFragment implements NotificationCe
progressDialog = null;
if (listAdapter != null) {
databaseSize = MessagesStorage.getInstance(currentAccount).getDatabaseSize();
updateDatabaseSize = true;
listAdapter.notifyDataSetChanged();
}
}
@ -709,7 +712,8 @@ public class CacheControlActivity extends BaseFragment implements NotificationCe
case 0:
TextSettingsCell textCell = (TextSettingsCell) holder.itemView;
if (position == databaseRow) {
textCell.setTextAndValue(LocaleController.getString("ClearLocalDatabase", R.string.ClearLocalDatabase), AndroidUtilities.formatFileSize(databaseSize), false);
textCell.setTextAndValue(LocaleController.getString("ClearLocalDatabase", R.string.ClearLocalDatabase), AndroidUtilities.formatFileSize(databaseSize), updateDatabaseSize, false);
updateDatabaseSize = false;
} else if (position == migrateOldFolderRow) {
textCell.setTextAndValue(LocaleController.getString("MigrateOldFolder", R.string.MigrateOldFolder), null, false);
}

View File

@ -87,6 +87,7 @@ public class CalendarActivity extends BaseFragment {
Paint blackoutPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private long dialogId;
private int topicId;
private boolean loading;
private boolean checkEnterItems;
private boolean inSelectionMode;
@ -140,6 +141,7 @@ public class CalendarActivity extends BaseFragment {
@Override
public boolean onFragmentCreate() {
dialogId = getArguments().getLong("dialog_id");
topicId = getArguments().getInt("topic_id");
calendarType = getArguments().getInt("type");
if (dialogId >= 0) {
@ -313,8 +315,8 @@ public class CalendarActivity extends BaseFragment {
public void run(boolean forAll) {
finishFragment();
if (parentLayout.fragmentsStack.size() >= 2) {
BaseFragment fragment = parentLayout.fragmentsStack.get(parentLayout.fragmentsStack.size() - 2);
if (parentLayout.getFragmentStack().size() >= 2) {
BaseFragment fragment = parentLayout.getFragmentStack().get(parentLayout.getFragmentStack().size() - 2);
if (fragment instanceof ChatActivity) {
((ChatActivity) fragment).deleteHistory(dateSelectedStart, dateSelectedEnd + 86400, forAll);
}
@ -600,6 +602,9 @@ public class CalendarActivity extends BaseFragment {
@SuppressLint("NotifyDataSetChanged")
@Override
public boolean onSingleTapUp(MotionEvent e) {
if (parentLayout == null) {
return false;
}
if (calendarType == TYPE_MEDIA_CALENDAR && messagesByDays != null) {
PeriodDay day = getDayAtCoord(e.getX(), e.getY());
if (day != null && day.messageObject != null && callback != null) {
@ -640,8 +645,8 @@ public class CalendarActivity extends BaseFragment {
}
} else {
PeriodDay day = getDayAtCoord(e.getX(), e.getY());
if (day != null && parentLayout.fragmentsStack.size() >= 2) {
BaseFragment fragment = parentLayout.fragmentsStack.get(parentLayout.fragmentsStack.size() - 2);
if (day != null && parentLayout.getFragmentStack().size() >= 2) {
BaseFragment fragment = parentLayout.getFragmentStack().get(parentLayout.getFragmentStack().size() - 2);
if (fragment instanceof ChatActivity) {
finishFragment();
((ChatActivity) fragment).jumpToDate(day.date);
@ -710,8 +715,8 @@ public class CalendarActivity extends BaseFragment {
cellJump.setTextAndIcon(LocaleController.getString("JumpToDate", R.string.JumpToDate), R.drawable.msg_message);
cellJump.setMinimumWidth(160);
cellJump.setOnClickListener(view -> {
if (parentLayout.fragmentsStack.size() >= 3) {
BaseFragment fragment = parentLayout.fragmentsStack.get(parentLayout.fragmentsStack.size() - 3);
if (parentLayout.getFragmentStack().size() >= 3) {
BaseFragment fragment = parentLayout.getFragmentStack().get(parentLayout.getFragmentStack().size() - 3);
if (fragment instanceof ChatActivity) {
AndroidUtilities.runOnUIThread(() -> {
finishFragment();
@ -740,8 +745,8 @@ public class CalendarActivity extends BaseFragment {
cellDelete.setTextAndIcon(LocaleController.getString("ClearHistory", R.string.ClearHistory), R.drawable.msg_delete);
cellDelete.setMinimumWidth(160);
cellDelete.setOnClickListener(view -> {
if (parentLayout.fragmentsStack.size() >= 3) {
BaseFragment fragment = parentLayout.fragmentsStack.get(parentLayout.fragmentsStack.size() - 3);
if (parentLayout.getFragmentStack().size() >= 3) {
BaseFragment fragment = parentLayout.getFragmentStack().get(parentLayout.getFragmentStack().size() - 3);
if (fragment instanceof ChatActivity) {
AlertsCreator.createClearDaysDialogAlert(CalendarActivity.this, 1, getMessagesController().getUser(dialogId), null, false, new MessagesStorage.BooleanCallback() {
@Override
@ -773,7 +778,7 @@ public class CalendarActivity extends BaseFragment {
});
blurredView.setVisibility(View.GONE);
blurredView.setFitsSystemWindows(true);
parentLayout.containerView.addView(blurredView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
parentLayout.getOverlayContainerView().addView(blurredView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
prepareBlurBitmap();
presentFragmentAsPreviewWithMenu(chatActivity, previewMenu);
@ -1261,13 +1266,13 @@ public class CalendarActivity extends BaseFragment {
}
@Override
protected void onTransitionAnimationStart(boolean isOpen, boolean backward) {
public void onTransitionAnimationStart(boolean isOpen, boolean backward) {
super.onTransitionAnimationStart(isOpen, backward);
isOpened = true;
}
@Override
protected void onTransitionAnimationProgress(boolean isOpen, float progress) {
public void onTransitionAnimationProgress(boolean isOpen, float progress) {
super.onTransitionAnimationProgress(isOpen, progress);
if (blurredView != null && blurredView.getVisibility() == View.VISIBLE) {
if (isOpen) {
@ -1279,7 +1284,7 @@ public class CalendarActivity extends BaseFragment {
}
@Override
protected void onTransitionAnimationEnd(boolean isOpen, boolean backward) {
public void onTransitionAnimationEnd(boolean isOpen, boolean backward) {
if (isOpen && blurredView != null && blurredView.getVisibility() == View.VISIBLE) {
blurredView.setVisibility(View.GONE);
blurredView.setBackground(null);
@ -1393,12 +1398,12 @@ public class CalendarActivity extends BaseFragment {
if (blurredView == null) {
return;
}
int w = (int) (parentLayout.getMeasuredWidth() / 6.0f);
int h = (int) (parentLayout.getMeasuredHeight() / 6.0f);
int w = (int) (parentLayout.getView().getMeasuredWidth() / 6.0f);
int h = (int) (parentLayout.getView().getMeasuredHeight() / 6.0f);
Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.scale(1.0f / 6.0f, 1.0f / 6.0f);
parentLayout.draw(canvas);
parentLayout.getView().draw(canvas);
Utilities.stackBlurBitmap(bitmap, Math.max(7, Math.max(w, h) / 180));
blurredView.setBackground(new BitmapDrawable(bitmap));
blurredView.setAlpha(0.0f);

View File

@ -1069,7 +1069,7 @@ public class CallLogActivity extends BaseFragment implements NotificationCenter.
cell.button.setTag(chat.id);
String text;
if (ChatObject.isChannel(chat) && !chat.megagroup) {
if (TextUtils.isEmpty(chat.username)) {
if (!ChatObject.isPublic(chat)) {
text = LocaleController.getString("ChannelPrivate", R.string.ChannelPrivate).toLowerCase();
} else {
text = LocaleController.getString("ChannelPublic", R.string.ChannelPublic).toLowerCase();
@ -1077,7 +1077,7 @@ public class CallLogActivity extends BaseFragment implements NotificationCenter.
} else {
if (chat.has_geo) {
text = LocaleController.getString("MegaLocation", R.string.MegaLocation);
} else if (TextUtils.isEmpty(chat.username)) {
} else if (!ChatObject.isPublic(chat)) {
text = LocaleController.getString("MegaPrivate", R.string.MegaPrivate).toLowerCase();
} else {
text = LocaleController.getString("MegaPublic", R.string.MegaPublic).toLowerCase();
@ -1115,7 +1115,7 @@ public class CallLogActivity extends BaseFragment implements NotificationCenter.
}
@Override
protected void onTransitionAnimationStart(boolean isOpen, boolean backward) {
public void onTransitionAnimationStart(boolean isOpen, boolean backward) {
super.onTransitionAnimationStart(isOpen, backward);
if (isOpen) {
openTransitionStarted = true;

View File

@ -15,6 +15,8 @@ import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.ImageFormat;
import android.graphics.Paint;
import android.graphics.Path;
@ -33,6 +35,7 @@ import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.ClickableSpan;
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.Gravity;
@ -75,9 +78,9 @@ import org.telegram.messenger.camera.CameraSession;
import org.telegram.messenger.camera.CameraView;
import org.telegram.messenger.camera.Size;
import org.telegram.ui.ActionBar.ActionBar;
import org.telegram.ui.ActionBar.ActionBarLayout;
import org.telegram.ui.ActionBar.BaseFragment;
import org.telegram.ui.ActionBar.BottomSheet;
import org.telegram.ui.ActionBar.INavigationLayout;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.ActionBar.ThemeDescription;
import org.telegram.ui.Components.AnimationProperties;
@ -154,15 +157,15 @@ public class CameraScanActivity extends BaseFragment {
}
}
public static ActionBarLayout[] showAsSheet(BaseFragment parentFragment, boolean gallery, int type, CameraScanActivityDelegate cameraDelegate) {
public static INavigationLayout[] showAsSheet(BaseFragment parentFragment, boolean gallery, int type, CameraScanActivityDelegate cameraDelegate) {
if (parentFragment == null || parentFragment.getParentActivity() == null) {
return null;
}
ActionBarLayout[] actionBarLayout = new ActionBarLayout[]{new ActionBarLayout(parentFragment.getParentActivity())};
INavigationLayout[] actionBarLayout = new INavigationLayout[]{INavigationLayout.newLayout(parentFragment.getParentActivity())};
BottomSheet bottomSheet = new BottomSheet(parentFragment.getParentActivity(), false) {
CameraScanActivity fragment;
{
actionBarLayout[0].init(new ArrayList<>());
actionBarLayout[0].setFragmentStack(new ArrayList<>());
fragment = new CameraScanActivity(type) {
@Override
public void finishFragment() {
@ -178,9 +181,9 @@ public class CameraScanActivity extends BaseFragment {
fragment.needGalleryButton = gallery;
actionBarLayout[0].addFragmentToStack(fragment);
actionBarLayout[0].showLastFragment();
actionBarLayout[0].setPadding(backgroundPaddingLeft, 0, backgroundPaddingLeft, 0);
actionBarLayout[0].getView().setPadding(backgroundPaddingLeft, 0, backgroundPaddingLeft, 0);
fragment.setDelegate(cameraDelegate);
containerView = actionBarLayout[0];
containerView = actionBarLayout[0].getView();
setApplyBottomPadding(false);
setApplyBottomPadding(false);
setOnDismissListener(dialog -> fragment.onFragmentDestroy());
@ -193,7 +196,7 @@ public class CameraScanActivity extends BaseFragment {
@Override
public void onBackPressed() {
if (actionBarLayout[0] == null || actionBarLayout[0].fragmentsStack.size() <= 1) {
if (actionBarLayout[0] == null || actionBarLayout[0].getFragmentStack().size() <= 1) {
super.onBackPressed();
} else {
actionBarLayout[0].onBackPressed();
@ -888,6 +891,11 @@ public class CameraScanActivity extends BaseFragment {
public void run() {
if (cameraView != null && !recognized && cameraView.getCameraSession() != null) {
handler.post(() -> {
try {
cameraView.focusToPoint(cameraView.getWidth() / 2, cameraView.getHeight() / 2, false);
} catch (Exception ignore) {
}
if (cameraView != null) {
processShot(cameraView.getTextureView().getBitmap());
}
@ -1011,6 +1019,52 @@ public class CameraScanActivity extends BaseFragment {
}
}
private Bitmap invert(Bitmap bitmap) {
int height = bitmap.getHeight();
int width = bitmap.getWidth();
Bitmap newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(newBitmap);
Paint paint = new Paint();
ColorMatrix matrixGrayscale = new ColorMatrix();
matrixGrayscale.setSaturation(0);
ColorMatrix matrixInvert = new ColorMatrix();
matrixInvert.set(new float[] {
-1.0f, 0.0f, 0.0f, 0.0f, 255.0f,
0.0f, -1.0f, 0.0f, 0.0f, 255.0f,
0.0f, 0.0f, -1.0f, 0.0f, 255.0f,
0.0f, 0.0f, 0.0f, 1.0f, 0.0f
});
matrixInvert.preConcat(matrixGrayscale);
paint.setColorFilter(new ColorMatrixColorFilter(matrixInvert));
canvas.drawBitmap(bitmap, 0, 0, paint);
return newBitmap;
}
private Bitmap monochrome(Bitmap bitmap, int threshold) {
int height = bitmap.getHeight();
int width = bitmap.getWidth();
Bitmap newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(newBitmap);
Paint paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(createThresholdMatrix(threshold)));
canvas.drawBitmap(bitmap, 0, 0, paint);
return newBitmap;
}
public static ColorMatrix createThresholdMatrix(int threshold) {
ColorMatrix matrix = new ColorMatrix(new float[] {
85.f, 85.f, 85.f, 0.f, -255.f * threshold,
85.f, 85.f, 85.f, 0.f, -255.f * threshold,
85.f, 85.f, 85.f, 0.f, -255.f * threshold,
0f, 0f, 0f, 1f, 0f
});
return matrix;
}
private class QrResult {
String text;
RectF bounds;
@ -1052,6 +1106,60 @@ public class CameraScanActivity extends BaseFragment {
}
bounds.set(minX, minY, maxX, maxY);
}
} else if (bitmap != null) {
Bitmap inverted = invert(bitmap);
bitmap.recycle();
frame = new Frame.Builder().setBitmap(inverted).build();
width = inverted.getWidth();
height = inverted.getHeight();
codes = visionQrReader.detect(frame);
if (codes != null && codes.size() > 0) {
Barcode code = codes.valueAt(0);
text = code.rawValue;
if (code.cornerPoints == null || code.cornerPoints.length == 0) {
bounds = null;
} else {
float minX = Float.MAX_VALUE,
maxX = Float.MIN_VALUE,
minY = Float.MAX_VALUE,
maxY = Float.MIN_VALUE;
for (Point point : code.cornerPoints) {
minX = Math.min(minX, point.x);
maxX = Math.max(maxX, point.x);
minY = Math.min(minY, point.y);
maxY = Math.max(maxY, point.y);
}
bounds.set(minX, minY, maxX, maxY);
}
} else {
Bitmap monochrome = monochrome(inverted, 90);
inverted.recycle();
frame = new Frame.Builder().setBitmap(monochrome).build();
width = inverted.getWidth();
height = inverted.getHeight();
codes = visionQrReader.detect(frame);
if (codes != null && codes.size() > 0) {
Barcode code = codes.valueAt(0);
text = code.rawValue;
if (code.cornerPoints == null || code.cornerPoints.length == 0) {
bounds = null;
} else {
float minX = Float.MAX_VALUE,
maxX = Float.MIN_VALUE,
minY = Float.MAX_VALUE,
maxY = Float.MIN_VALUE;
for (Point point : code.cornerPoints) {
minX = Math.min(minX, point.x);
maxX = Math.max(maxX, point.x);
minY = Math.min(minY, point.y);
maxY = Math.max(maxY, point.y);
}
bounds.set(minX, minY, maxX, maxY);
}
} else {
text = null;
}
}
} else {
text = null;
}

View File

@ -40,6 +40,7 @@ import android.widget.FrameLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.core.view.GestureDetectorCompat;
import androidx.recyclerview.widget.RecyclerView;
import org.telegram.messenger.AndroidUtilities;
@ -93,6 +94,8 @@ public class AboutLinkCell extends FrameLayout {
private boolean needSpace = false;
private boolean moreButtonDisabled;
private GestureDetectorCompat gestureDetector;
public AboutLinkCell(Context context, BaseFragment fragment) {
this(context, fragment, null);
}
@ -103,59 +106,9 @@ public class AboutLinkCell extends FrameLayout {
this.resourcesProvider = resourcesProvider;
parentFragment = fragment;
container = new FrameLayout(context) {
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
boolean result = false;
if (textLayout != null || nextLinesLayouts != null) {
if (event.getAction() == MotionEvent.ACTION_DOWN || pressedLink != null && event.getAction() == MotionEvent.ACTION_UP) {
if (x >= showMoreTextView.getLeft() && x <= showMoreTextView.getRight() &&
y >= showMoreTextView.getTop() && y <= showMoreTextView.getBottom()) {
return super.onTouchEvent(event);
}
if (getMeasuredWidth() > 0 && x > getMeasuredWidth() - AndroidUtilities.dp(23)) {
return super.onTouchEvent(event);
}
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (firstThreeLinesLayout != null && expandT < 1 && shouldExpand) {
if (checkTouchTextLayout(firstThreeLinesLayout, textX, textY, x, y)) {
result = true;
} else if (nextLinesLayouts != null) {
for (int i = 0; i < nextLinesLayouts.length; ++i) {
if (checkTouchTextLayout(nextLinesLayouts[i], nextLinesLayoutsPositions[i].x, nextLinesLayoutsPositions[i].y, x, y)) {
result = true;
break;
}
}
}
} else if (checkTouchTextLayout(textLayout, textX, textY, x, y)) {
result = true;
}
if (!result) {
resetPressedLink();
}
} else if (pressedLink != null) {
try {
onLinkClick((ClickableSpan) pressedLink.getSpan());
} catch (Exception e) {
FileLog.e(e);
}
resetPressedLink();
result = true;
}
} else if (event.getAction() == MotionEvent.ACTION_CANCEL) {
resetPressedLink();
}
}
return result || super.onTouchEvent(event);
}
};
container = new FrameLayout(context);
container.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
links = new LinkSpanDrawable.LinkCollector(container);
container.setClickable(true);
rippleBackground = Theme.createRadSelectorDrawable(Theme.getColor(Theme.key_listSelector, resourcesProvider), 0, 0);
valueTextView = new TextView(context);
@ -191,7 +144,7 @@ public class AboutLinkCell extends FrameLayout {
if (wasPressed != pressed) {
invalidate();
}
return super.onTouchEvent(event);
return pressed || super.onTouchEvent(event);
}
@Override
@ -230,6 +183,46 @@ public class AboutLinkCell extends FrameLayout {
setWillNotDraw(false);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
if (showMoreTextView.getVisibility() == View.VISIBLE &&
x >= showMoreTextBackgroundView.getLeft() && x <= showMoreTextBackgroundView.getRight() &&
y >= showMoreTextBackgroundView.getTop() && y <= showMoreTextBackgroundView.getBottom()
) {
// event.offsetLocation(showMoreTextBackgroundView.getLeft(), showMoreTextBackgroundView.getTop());
return false;
}
boolean result = false;
if (textLayout != null || nextLinesLayouts != null) {
if (event.getAction() == MotionEvent.ACTION_DOWN || pressedLink != null && event.getAction() == MotionEvent.ACTION_UP) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
resetPressedLink();
LinkSpanDrawable link = hitLink(x, y);
if (link != null) {
result = true;
links.addLink(pressedLink = link);
AndroidUtilities.runOnUIThread(longPressedRunnable, ViewConfiguration.getLongPressTimeout());
}
} else if (pressedLink != null) {
try {
onLinkClick((ClickableSpan) pressedLink.getSpan());
} catch (Exception e) {
FileLog.e(e);
}
resetPressedLink();
result = true;
}
} else if (event.getAction() == MotionEvent.ACTION_CANCEL) {
resetPressedLink();
}
}
return result || super.onTouchEvent(event);
}
private void setShowMoreMarginBottom(int marginBottom) {
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) showMoreTextBackgroundView.getLayoutParams();
if (lp.bottomMargin != marginBottom) {
@ -325,11 +318,6 @@ public class AboutLinkCell extends FrameLayout {
canvas.restore();
}
@Override
public void setOnClickListener(@Nullable OnClickListener l) {
container.setOnClickListener(l);
}
protected void didPressUrl(String url) {
}
@ -426,7 +414,34 @@ public class AboutLinkCell extends FrameLayout {
}
};
private boolean checkTouchTextLayout(StaticLayout textLayout, int textX, int textY, int ex, int ey) {
private LinkSpanDrawable hitLink(int x, int y) {
if (x >= showMoreTextView.getLeft() && x <= showMoreTextView.getRight() &&
y >= showMoreTextView.getTop() && y <= showMoreTextView.getBottom()) {
return null;
}
if (getMeasuredWidth() > 0 && x > getMeasuredWidth() - AndroidUtilities.dp(23)) {
return null;
}
LinkSpanDrawable link;
if (firstThreeLinesLayout != null && expandT < 1 && shouldExpand) {
if ((link = checkTouchTextLayout(firstThreeLinesLayout, textX, textY, x, y)) != null) {
return link;
}
if (nextLinesLayouts != null) {
for (int i = 0; i < nextLinesLayouts.length; ++i) {
if ((link = checkTouchTextLayout(nextLinesLayouts[i], nextLinesLayoutsPositions[i].x, nextLinesLayoutsPositions[i].y, x, y)) != null) {
return link;
}
}
}
}
if ((link = checkTouchTextLayout(textLayout, textX, textY, x, y)) != null) {
return link;
}
return null;
}
private LinkSpanDrawable checkTouchTextLayout(StaticLayout textLayout, int textX, int textY, int ex, int ey) {
try {
int x = (int) (ex - textX);
int y = (int) (ey - textY);
@ -438,26 +453,19 @@ public class AboutLinkCell extends FrameLayout {
Spannable buffer = (Spannable) textLayout.getText();
ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);
if (link.length != 0 && !AndroidUtilities.isAccessibilityScreenReaderEnabled()) {
resetPressedLink();
pressedLink = new LinkSpanDrawable(link[0], parentFragment.getResourceProvider(), ex, ey);
links.addLink(pressedLink);
int start = buffer.getSpanStart(pressedLink.getSpan());
int end = buffer.getSpanEnd(pressedLink.getSpan());
LinkPath path = pressedLink.obtainNewPath();
LinkSpanDrawable linkDrawable = new LinkSpanDrawable(link[0], parentFragment.getResourceProvider(), ex, ey);
int start = buffer.getSpanStart(link[0]);
int end = buffer.getSpanEnd(link[0]);
LinkPath path = linkDrawable.obtainNewPath();
path.setCurrentLayout(textLayout, start, textY);
textLayout.getSelectionPath(start, end, path);
AndroidUtilities.runOnUIThread(longPressedRunnable, ViewConfiguration.getLongPressTimeout());
return true;
} else {
return false;
return linkDrawable;
}
} else {
return false;
}
} catch (Exception e) {
FileLog.e(e);
return false;
}
return null;
}
private void onLinkClick(ClickableSpan pressedLink) {

View File

@ -18,6 +18,7 @@ import android.widget.FrameLayout;
import android.widget.ImageView;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.ChatObject;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.MessagesController;
import org.telegram.messenger.R;
@ -89,7 +90,7 @@ public class AdminedChannelCell extends FrameLayout {
currentChannel = channel;
avatarDrawable.setInfo(channel);
nameTextView.setText(channel.title);
SpannableStringBuilder stringBuilder = new SpannableStringBuilder(url + channel.username);
SpannableStringBuilder stringBuilder = new SpannableStringBuilder(url + ChatObject.getPublicUsername(channel));
stringBuilder.setSpan(new URLSpanNoUnderline(""), url.length(), stringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
statusTextView.setText(stringBuilder);
avatarImageView.setForUserOrChat(channel, avatarDrawable);

View File

@ -63,6 +63,12 @@ public abstract class BaseCell extends ViewGroup {
setDrawableBounds(drawable, (int) x, (int) y, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
}
public static float setDrawableBounds(Drawable drawable, float x, float y, float h) {
float w = drawable.getIntrinsicWidth() * h / drawable.getIntrinsicHeight();
setDrawableBounds(drawable, (int) x, (int) y, (int) w, (int) h);
return w;
}
public static void setDrawableBounds(Drawable drawable, int x, int y, int w, int h) {
if (drawable != null) {
drawable.setBounds(x, y, x + w, y + h);

View File

@ -47,6 +47,7 @@ import org.telegram.messenger.ImageReceiver;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.MediaDataController;
import org.telegram.messenger.MessageObject;
import org.telegram.messenger.MessagesController;
import org.telegram.messenger.NotificationCenter;
import org.telegram.messenger.R;
import org.telegram.messenger.SharedConfig;
@ -56,10 +57,12 @@ import org.telegram.messenger.browser.Browser;
import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.ActionBar;
import org.telegram.ui.ActionBar.BaseFragment;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Components.AnimatedEmojiDrawable;
import org.telegram.ui.Components.AnimatedEmojiSpan;
import org.telegram.ui.Components.AvatarDrawable;
import org.telegram.ui.Components.Forum.ForumUtilities;
import org.telegram.ui.Components.Premium.StarParticlesView;
import org.telegram.ui.Components.RLottieDrawable;
import org.telegram.ui.Components.TypefaceSpan;
@ -143,6 +146,22 @@ public class ChatActionCell extends BaseCell implements DownloadController.FileD
default void needShowEffectOverlay(ChatActionCell cell, TLRPC.Document document, TLRPC.VideoSize videoSize) {
}
default BaseFragment getBaseFragment() {
return null;
}
default long getDialogId() {
return 0;
}
default int getTopicId() {
return 0;
}
default boolean canDrawOutboundsContent() {
return true;
}
}
public interface ThemeDelegate extends Theme.ResourcesProvider {
@ -453,7 +472,7 @@ public class ChatActionCell extends BaseCell implements DownloadController.FileD
MediaDataController.getInstance(currentAccount).loadStickersByEmojiOrName(packName, false, set == null);
}
}
} else if (messageObject.type == 11) {
} else if (messageObject.type == MessageObject.TYPE_ACTION_PHOTO) {
imageReceiver.setAllowStartLottieAnimation(true);
imageReceiver.setDelegate(null);
imageReceiver.setRoundRadius(AndroidUtilities.roundMessageSize / 2);
@ -499,6 +518,7 @@ public class ChatActionCell extends BaseCell implements DownloadController.FileD
imageReceiver.setImageBitmap((Bitmap) null);
}
rippleView.setVisibility(messageObject.type == MessageObject.TYPE_GIFT_PREMIUM ? VISIBLE : GONE);
ForumUtilities.applyTopicToMessage(messageObject);
requestLayout();
}
@ -548,7 +568,7 @@ public class ChatActionCell extends BaseCell implements DownloadController.FileD
imageReceiver.onAttachedToWindow();
setStarsPaused(false);
animatedEmojiStack = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, this, animatedEmojiStack, textLayout);
animatedEmojiStack = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, this, canDrawInParent && (delegate != null && !delegate.canDrawOutboundsContent()), animatedEmojiStack, textLayout);
NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.didUpdatePremiumGiftStickers);
NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.diceStickersDidLoad);
}
@ -581,7 +601,7 @@ public class ChatActionCell extends BaseCell implements DownloadController.FileD
boolean result = false;
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (delegate != null) {
if ((messageObject.type == 11 || messageObject.type == MessageObject.TYPE_GIFT_PREMIUM) && imageReceiver.isInsideImage(x, y)) {
if ((messageObject.type == MessageObject.TYPE_ACTION_PHOTO || messageObject.type == MessageObject.TYPE_GIFT_PREMIUM) && imageReceiver.isInsideImage(x, y)) {
imagePressed = true;
result = true;
}
@ -694,7 +714,14 @@ public class ChatActionCell extends BaseCell implements DownloadController.FileD
private void openLink(CharacterStyle link) {
if (delegate != null && link instanceof URLSpan) {
String url = ((URLSpan) link).getURL();
if (url.startsWith("invite") && pressedLink instanceof URLSpanNoUnderline) {
if (url.startsWith("topic") && pressedLink instanceof URLSpanNoUnderline) {
URLSpanNoUnderline spanNoUnderline = (URLSpanNoUnderline) pressedLink;
TLObject object = spanNoUnderline.getObject();
if (object instanceof TLRPC.TL_forumTopic) {
TLRPC.TL_forumTopic forumTopic = (TLRPC.TL_forumTopic) object;
ForumUtilities.openTopic(delegate.getBaseFragment(), -delegate.getDialogId(), forumTopic, 0);
}
} else if (url.startsWith("invite") && pressedLink instanceof URLSpanNoUnderline) {
URLSpanNoUnderline spanNoUnderline = (URLSpanNoUnderline) pressedLink;
TLObject object = spanNoUnderline.getObject();
if (object instanceof TLRPC.TL_chatInviteExported) {
@ -738,9 +765,10 @@ public class ChatActionCell extends BaseCell implements DownloadController.FileD
spoilersPool.addAll(spoilers);
spoilers.clear();
if (text instanceof Spannable)
if (text instanceof Spannable) {
SpoilerEffect.addSpoilers(this, textLayout, (Spannable) text, spoilersPool, spoilers);
animatedEmojiStack = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, this, animatedEmojiStack, textLayout);
}
animatedEmojiStack = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, this, canDrawInParent && (delegate != null && !delegate.canDrawOutboundsContent()), animatedEmojiStack, textLayout);
textHeight = 0;
textWidth = 0;
@ -788,7 +816,7 @@ public class ChatActionCell extends BaseCell implements DownloadController.FileD
}
int additionalHeight = 0;
if (messageObject != null) {
if (messageObject.type == 11) {
if (messageObject.type == MessageObject.TYPE_ACTION_PHOTO) {
additionalHeight = AndroidUtilities.roundMessageSize + AndroidUtilities.dp(10);
} else if (messageObject.type == MessageObject.TYPE_GIFT_PREMIUM) {
additionalHeight = giftRectSize + AndroidUtilities.dp(12);
@ -813,26 +841,32 @@ public class ChatActionCell extends BaseCell implements DownloadController.FileD
}
private void buildLayout() {
CharSequence text;
CharSequence text = null;
MessageObject messageObject = currentMessageObject;
if (messageObject != null) {
if (messageObject.messageOwner != null && messageObject.messageOwner.media != null && messageObject.messageOwner.media.ttl_seconds != 0) {
if (messageObject.messageOwner.media.photo instanceof TLRPC.TL_photoEmpty) {
text = LocaleController.getString("AttachPhotoExpired", R.string.AttachPhotoExpired);
} else if (messageObject.messageOwner.media.document instanceof TLRPC.TL_documentEmpty) {
text = LocaleController.getString("AttachVideoExpired", R.string.AttachVideoExpired);
if (delegate.getTopicId() == 0 && MessageObject.isTopicActionMessage(messageObject)) {
TLRPC.TL_forumTopic topic = MessagesController.getInstance(currentAccount).getTopicsController().findTopic(-messageObject.getDialogId(), MessageObject.getTopicId(messageObject.messageOwner));
text = ForumUtilities.createActionTextWithTopic(topic, messageObject);
}
if (text == null) {
if (messageObject.messageOwner != null && messageObject.messageOwner.media != null && messageObject.messageOwner.media.ttl_seconds != 0) {
if (messageObject.messageOwner.media.photo instanceof TLRPC.TL_photoEmpty) {
text = LocaleController.getString("AttachPhotoExpired", R.string.AttachPhotoExpired);
} else if (messageObject.messageOwner.media.document instanceof TLRPC.TL_documentEmpty) {
text = LocaleController.getString("AttachVideoExpired", R.string.AttachVideoExpired);
} else {
text = AnimatedEmojiSpan.cloneSpans(messageObject.messageText);
}
} else {
text = AnimatedEmojiSpan.cloneSpans(messageObject.messageText);
}
} else {
text = AnimatedEmojiSpan.cloneSpans(messageObject.messageText);
}
} else {
text = customText;
}
createLayout(text, previousWidth);
if (messageObject != null) {
if (messageObject.type == 11) {
if (messageObject.type == MessageObject.TYPE_ACTION_PHOTO) {
imageReceiver.setImageCoords((previousWidth - AndroidUtilities.roundMessageSize) / 2f, textHeight + AndroidUtilities.dp(19), AndroidUtilities.roundMessageSize, AndroidUtilities.roundMessageSize);
} else if (messageObject.type == MessageObject.TYPE_GIFT_PREMIUM) {
createGiftPremiumLayouts(LocaleController.getString(R.string.ActionGiftPremiumTitle), LocaleController.formatString(R.string.ActionGiftPremiumSubtitle, LocaleController.formatPluralString("Months", messageObject.messageOwner.action.months)), LocaleController.getString(R.string.ActionGiftPremiumView), giftRectSize);
@ -882,7 +916,7 @@ public class ChatActionCell extends BaseCell implements DownloadController.FileD
}
}
}
if (messageObject != null && (messageObject.type == 11 || messageObject.type == MessageObject.TYPE_GIFT_PREMIUM)) {
if (messageObject != null && (messageObject.type == MessageObject.TYPE_ACTION_PHOTO || messageObject.type == MessageObject.TYPE_GIFT_PREMIUM)) {
imageReceiver.draw(canvas);
}
@ -900,8 +934,10 @@ public class ChatActionCell extends BaseCell implements DownloadController.FileD
}
canvas.save();
SpoilerEffect.clipOutCanvas(canvas, spoilers);
AnimatedEmojiSpan.drawAnimatedEmojis(canvas, textLayout, animatedEmojiStack, 0, spoilers, 0, 0, 0, 1f);
textLayout.draw(canvas);
if (delegate == null || delegate.canDrawOutboundsContent()) {
AnimatedEmojiSpan.drawAnimatedEmojis(canvas, textLayout, animatedEmojiStack, 0, spoilers, 0, 0, 0, 1f);
}
canvas.restore();
for (SpoilerEffect eff : spoilers) {
@ -1157,7 +1193,7 @@ public class ChatActionCell extends BaseCell implements DownloadController.FileD
@Override
public void onSuccessDownload(String fileName) {
MessageObject messageObject = currentMessageObject;
if (messageObject != null && messageObject.type == 11) {
if (messageObject != null && messageObject.type == MessageObject.TYPE_ACTION_PHOTO) {
TLRPC.PhotoSize strippedPhotoSize = null;
for (int a = 0, N = messageObject.photoThumbs.size(); a < N; a++) {
TLRPC.PhotoSize photoSize = messageObject.photoThumbs.get(a);
@ -1241,4 +1277,11 @@ public class ChatActionCell extends BaseCell implements DownloadController.FileD
Paint paint = themeDelegate != null ? themeDelegate.getPaint(paintKey) : null;
return paint != null ? paint : Theme.getThemePaint(paintKey);
}
public void drawOutboundsContent(Canvas canvas) {
canvas.save();
canvas.translate(textXLeft, textY);
AnimatedEmojiSpan.drawAnimatedEmojis(canvas, textLayout, animatedEmojiStack, 0, spoilers, 0, 0, 0, 1f);
canvas.restore();
}
}

View File

@ -143,7 +143,7 @@ public class ContextLinkCell extends FrameLayout implements DownloadController.F
linkImageView = new ImageReceiver(this);
linkImageView.setLayerNum(1);
linkImageView.setUseSharedAnimationQueue(true);
letterDrawable = new LetterDrawable(resourcesProvider);
letterDrawable = new LetterDrawable(resourcesProvider, LetterDrawable.STYLE_DEFAULT);
radialProgress = new RadialProgress2(this);
TAG = DownloadController.getInstance(currentAccount).generateObserverTag();
setFocusable(true);

View File

@ -15,7 +15,6 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
@ -29,15 +28,12 @@ import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.LinearInterpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.telegram.PhoneFormat.PhoneFormat;
import org.telegram.messenger.AndroidUtilities;
@ -49,6 +45,7 @@ import org.telegram.messenger.ImageReceiver;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.MediaDataController;
import org.telegram.messenger.MessageObject;
import org.telegram.messenger.MessagesController;
import org.telegram.messenger.NotificationCenter;
import org.telegram.messenger.R;
import org.telegram.messenger.UserConfig;
@ -59,7 +56,6 @@ import org.telegram.ui.ActionBar.DrawerLayoutContainer;
import org.telegram.ui.ActionBar.SimpleTextView;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Components.AnimatedEmojiDrawable;
import org.telegram.ui.Components.AnimatedFloat;
import org.telegram.ui.Components.AvatarDrawable;
import org.telegram.ui.Components.BackupImageView;
import org.telegram.ui.Components.CubicBezierInterpolator;
@ -83,7 +79,7 @@ public class DrawerProfileCell extends FrameLayout implements NotificationCenter
private ImageView shadowView;
private ImageView arrowView;
private RLottieImageView darkThemeView;
private RLottieDrawable sunDrawable;
private static RLottieDrawable sunDrawable;
private boolean updateRightDrawable = true;
private AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable status;
private AnimatedStatusView animatedStatus;
@ -139,7 +135,9 @@ public class DrawerProfileCell extends FrameLayout implements NotificationCenter
nameTextView.setTextSize(15);
nameTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
nameTextView.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.BOTTOM, 16, 0, 76, 28));
nameTextView.setEllipsizeByGradient(true);
nameTextView.setRightDrawableOutside(true);
addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.BOTTOM, 16, 0, 52, 28));
phoneTextView = new TextView(context);
phoneTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13);
@ -147,7 +145,7 @@ public class DrawerProfileCell extends FrameLayout implements NotificationCenter
phoneTextView.setMaxLines(1);
phoneTextView.setSingleLine(true);
phoneTextView.setGravity(Gravity.LEFT);
addView(phoneTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.BOTTOM, 16, 0, 76, 9));
addView(phoneTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.BOTTOM, 16, 0, 52, 9));
arrowView = new ImageView(context);
arrowView.setScaleType(ImageView.ScaleType.CENTER);
@ -155,14 +153,18 @@ public class DrawerProfileCell extends FrameLayout implements NotificationCenter
addView(arrowView, LayoutHelper.createFrame(59, 59, Gravity.RIGHT | Gravity.BOTTOM));
setArrowState(false);
sunDrawable = new RLottieDrawable(R.raw.sun, "" + R.raw.sun, AndroidUtilities.dp(28), AndroidUtilities.dp(28), true, null);
if (Theme.isCurrentThemeDay()) {
sunDrawable.setCustomEndFrame(36);
} else {
sunDrawable.setCustomEndFrame(0);
sunDrawable.setCurrentFrame(36);
boolean playDrawable;
if (playDrawable = sunDrawable == null) {
sunDrawable = new RLottieDrawable(R.raw.sun, "" + R.raw.sun, AndroidUtilities.dp(28), AndroidUtilities.dp(28), true, null);
sunDrawable.setPlayInDirectionOfCustomEndFrame(true);
if (Theme.isCurrentThemeDay()) {
sunDrawable.setCustomEndFrame(0);
sunDrawable.setCurrentFrame(0);
} else {
sunDrawable.setCurrentFrame(35);
sunDrawable.setCustomEndFrame(36);
}
}
sunDrawable.setPlayInDirectionOfCustomEndFrame(true);
darkThemeView = new RLottieImageView(context) {
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
@ -189,6 +191,9 @@ public class DrawerProfileCell extends FrameLayout implements NotificationCenter
darkThemeView.setBackgroundDrawable(Theme.createSelectorDrawable(darkThemeBackgroundColor = Theme.getColor(Theme.key_listSelector), 1, AndroidUtilities.dp(17)));
Theme.setRippleDrawableForceSoftware((RippleDrawable) darkThemeView.getBackground());
}
if (!playDrawable && sunDrawable.getCustomEndFrame() != sunDrawable.getCurrentFrame()) {
darkThemeView.playAnimation();
}
darkThemeView.setOnClickListener(v -> {
if (switchingTheme) {
return;
@ -719,8 +724,15 @@ public class DrawerProfileCell extends FrameLayout implements NotificationCenter
nameTextView.invalidate();
} else if (id == NotificationCenter.userEmojiStatusUpdated) {
setUser((TLRPC.User) args[0], accountsShown);
} else if (id == NotificationCenter.currentUserPremiumStatusChanged || id == NotificationCenter.updateInterfaces) {
} else if (id == NotificationCenter.currentUserPremiumStatusChanged) {
setUser(UserConfig.getInstance(UserConfig.selectedAccount).getCurrentUser(), accountsShown);
} else if (id == NotificationCenter.updateInterfaces) {
int flags = (int) args[0];
if ((flags & MessagesController.UPDATE_MASK_NAME) != 0 || (flags & MessagesController.UPDATE_MASK_AVATAR) != 0 ||
(flags & MessagesController.UPDATE_MASK_STATUS) != 0 || (flags & MessagesController.UPDATE_MASK_PHONE) != 0 ||
(flags & MessagesController.UPDATE_MASK_EMOJI_STATUS) != 0) {
setUser(UserConfig.getInstance(UserConfig.selectedAccount).getCurrentUser(), accountsShown);
}
}
}

View File

@ -50,7 +50,7 @@ public class DrawerUserCell extends FrameLayout implements NotificationCenter.No
super(context);
avatarDrawable = new AvatarDrawable();
avatarDrawable.setTextSize(AndroidUtilities.dp(12));
avatarDrawable.setTextSize(AndroidUtilities.dp(20));
imageView = new BackupImageView(context);
imageView.setRoundRadius(AndroidUtilities.dp(18));
@ -63,7 +63,8 @@ public class DrawerUserCell extends FrameLayout implements NotificationCenter.No
textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
textView.setMaxLines(1);
textView.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.CENTER_VERTICAL, 72, 0, 60, 0));
textView.setEllipsizeByGradient(24);
addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.CENTER_VERTICAL, 72, 0, 14, 0));
status = new AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable(textView, AndroidUtilities.dp(20));
textView.setRightDrawable(status);
@ -141,14 +142,18 @@ public class DrawerUserCell extends FrameLayout implements NotificationCenter.No
if (user.emoji_status instanceof TLRPC.TL_emojiStatusUntil && ((TLRPC.TL_emojiStatusUntil) user.emoji_status).until > (int) (System.currentTimeMillis() / 1000)) {
textView.setDrawablePadding(AndroidUtilities.dp(4));
status.set(((TLRPC.TL_emojiStatusUntil) user.emoji_status).document_id, true);
textView.setRightDrawableOutside(true);
} else if (user.emoji_status instanceof TLRPC.TL_emojiStatus) {
textView.setDrawablePadding(AndroidUtilities.dp(4));
status.set(((TLRPC.TL_emojiStatus) user.emoji_status).document_id, true);
textView.setRightDrawableOutside(true);
} else if (MessagesController.getInstance(account).isPremiumUser(user)) {
textView.setDrawablePadding(AndroidUtilities.dp(6));
status.set(PremiumGradient.getInstance().premiumStarDrawableMini, true);
textView.setRightDrawableOutside(true);
} else {
status.set((Drawable) null, true);
textView.setRightDrawableOutside(false);
}
status.setColor(Theme.getColor(Theme.key_chats_verifiedBackground));
imageView.getImageReceiver().setCurrentAccount(account);
@ -163,10 +168,12 @@ public class DrawerUserCell extends FrameLayout implements NotificationCenter.No
@Override
protected void onDraw(Canvas canvas) {
if (UserConfig.getActivatedAccountsCount() <= 1 || !NotificationsController.getInstance(accountNumber).showBadgeNumber) {
textView.setRightPadding(0);
return;
}
int counter = MessagesStorage.getInstance(accountNumber).getMainUnreadCount();
if (counter <= 0) {
textView.setRightPadding(0);
return;
}
@ -181,6 +188,8 @@ public class DrawerUserCell extends FrameLayout implements NotificationCenter.No
canvas.drawRoundRect(rect, 11.5f * AndroidUtilities.density, 11.5f * AndroidUtilities.density, Theme.dialogs_countPaint);
canvas.drawText(text, rect.left + (rect.width() - textWidth) / 2, countTop + AndroidUtilities.dp(16), Theme.dialogs_countTextPaint);
textView.setRightPadding(countWidth + AndroidUtilities.dp(14 + 12));
}
@Override

View File

@ -375,7 +375,7 @@ public class GroupCreateUserCell extends FrameLayout {
}
} else if (currentChat.has_geo) {
statusTextView.setText(LocaleController.getString("MegaLocation", R.string.MegaLocation));
} else if (TextUtils.isEmpty(currentChat.username)) {
} else if (!ChatObject.isPublic(currentChat)) {
if (ChatObject.isChannel(currentChat) && !currentChat.megagroup) {
statusTextView.setText(LocaleController.getString("ChannelPrivate", R.string.ChannelPrivate));
} else {

View File

@ -316,7 +316,7 @@ public class ManageChatUserCell extends FrameLayout {
}
} else if (currentChat.has_geo) {
statusTextView.setText(LocaleController.getString("MegaLocation", R.string.MegaLocation));
} else if (TextUtils.isEmpty(currentChat.username)) {
} else if (!ChatObject.isPublic(currentChat)) {
statusTextView.setText(LocaleController.getString("MegaPrivate", R.string.MegaPrivate));
} else {
statusTextView.setText(LocaleController.getString("MegaPublic", R.string.MegaPublic));

View File

@ -19,6 +19,7 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.ChatObject;
import org.telegram.messenger.MediaDataController;
import org.telegram.messenger.Emoji;
import org.telegram.messenger.UserConfig;
@ -131,8 +132,9 @@ public class MentionCell extends LinearLayout {
imageView.setImageDrawable(avatarDrawable);
}
nameTextView.setText(chat.title);
if (chat.username != null) {
usernameTextView.setText("@" + chat.username);
String username;
if ((username = ChatObject.getPublicUsername(chat)) != null) {
usernameTextView.setText("@" + username);
} else {
usernameTextView.setText("");
}

View File

@ -18,8 +18,6 @@ import android.text.TextPaint;
import android.text.TextUtils;
import android.view.accessibility.AccessibilityNodeInfo;
import androidx.annotation.NonNull;
import org.telegram.PhoneFormat.PhoneFormat;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.ChatObject;
@ -44,6 +42,8 @@ import org.telegram.ui.Components.CombinedDrawable;
import org.telegram.ui.Components.Premium.PremiumGradient;
import org.telegram.ui.NotificationsSettingsActivity;
import java.util.Locale;
public class ProfileSearchCell extends BaseCell implements NotificationCenter.NotificationCenterDelegate {
private CharSequence currentName;
@ -338,9 +338,10 @@ public class ProfileSearchCell extends BaseCell implements NotificationCenter.No
if (drawCount) {
TLRPC.Dialog dialog = MessagesController.getInstance(currentAccount).dialogs_dict.get(dialog_id);
if (dialog != null && dialog.unread_count != 0) {
lastUnreadCount = dialog.unread_count;
String countString = String.format("%d", dialog.unread_count);
int unreadCount = MessagesController.getInstance(currentAccount).getDialogUnreadCount(dialog);
if (unreadCount != 0) {
lastUnreadCount = unreadCount;
String countString = String.format(Locale.US, "%d", unreadCount);
countWidth = Math.max(AndroidUtilities.dp(12), (int) Math.ceil(Theme.dialogs_countTextPaint.measureText(countString)));
countLayout = new StaticLayout(countString, Theme.dialogs_countTextPaint, countWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false);
int w = countWidth + AndroidUtilities.dp(18);
@ -410,9 +411,9 @@ public class ProfileSearchCell extends BaseCell implements NotificationCenter.No
} else {
if (ChatObject.isChannel(chat) && !chat.megagroup) {
if (chat.participants_count != 0) {
statusString = LocaleController.formatPluralString("Subscribers", chat.participants_count);
statusString = LocaleController.formatPluralStringComma("Subscribers", chat.participants_count);
} else {
if (TextUtils.isEmpty(chat.username)) {
if (!ChatObject.isPublic(chat)) {
statusString = LocaleController.getString("ChannelPrivate", R.string.ChannelPrivate).toLowerCase();
} else {
statusString = LocaleController.getString("ChannelPublic", R.string.ChannelPublic).toLowerCase();
@ -420,11 +421,11 @@ public class ProfileSearchCell extends BaseCell implements NotificationCenter.No
}
} else {
if (chat.participants_count != 0) {
statusString = LocaleController.formatPluralString("Members", chat.participants_count);
statusString = LocaleController.formatPluralStringComma("Members", chat.participants_count);
} else {
if (chat.has_geo) {
statusString = LocaleController.getString("MegaLocation", R.string.MegaLocation);
} else if (TextUtils.isEmpty(chat.username)) {
} else if (!ChatObject.isPublic(chat)) {
statusString = LocaleController.getString("MegaPrivate", R.string.MegaPrivate).toLowerCase();
} else {
statusString = LocaleController.getString("MegaPublic", R.string.MegaPublic).toLowerCase();
@ -554,6 +555,7 @@ public class ProfileSearchCell extends BaseCell implements NotificationCenter.No
avatarImage.setImage(null, null, avatarDrawable, null, null, 0);
}
avatarImage.setRoundRadius(chat != null && chat.forum ? AndroidUtilities.dp(16) : AndroidUtilities.dp(23));
if (mask != 0) {
boolean continueUpdate = false;
if ((mask & MessagesController.UPDATE_MASK_AVATAR) != 0 && user != null || (mask & MessagesController.UPDATE_MASK_CHAT_AVATAR) != 0 && chat != null) {
@ -586,7 +588,7 @@ public class ProfileSearchCell extends BaseCell implements NotificationCenter.No
}
if (!continueUpdate && drawCount && (mask & MessagesController.UPDATE_MASK_READ_DIALOG_MESSAGE) != 0) {
TLRPC.Dialog dialog = MessagesController.getInstance(currentAccount).dialogs_dict.get(dialog_id);
if (dialog != null && dialog.unread_count != lastUnreadCount) {
if (dialog != null && MessagesController.getInstance(currentAccount).getDialogUnreadCount(dialog) != lastUnreadCount) {
continueUpdate = true;
}
}
@ -667,7 +669,7 @@ public class ProfileSearchCell extends BaseCell implements NotificationCenter.No
if (countLayout != null) {
int x = countLeft - AndroidUtilities.dp(5.5f);
rect.set(x, countTop, x + countWidth + AndroidUtilities.dp(11), countTop + AndroidUtilities.dp(23));
canvas.drawRoundRect(rect, 11.5f * AndroidUtilities.density, 11.5f * AndroidUtilities.density, MessagesController.getInstance(currentAccount).isDialogMuted(dialog_id) ? Theme.dialogs_countGrayPaint : Theme.dialogs_countPaint);
canvas.drawRoundRect(rect, 11.5f * AndroidUtilities.density, 11.5f * AndroidUtilities.density, MessagesController.getInstance(currentAccount).isDialogMuted(dialog_id, 0) ? Theme.dialogs_countGrayPaint : Theme.dialogs_countPaint);
canvas.save();
canvas.translate(countLeft, countTop + AndroidUtilities.dp(4));
countLayout.draw(canvas);

View File

@ -11,6 +11,7 @@ package org.telegram.ui.Cells;
import android.content.Context;
import android.graphics.Canvas;
import android.os.SystemClock;
import android.text.Layout;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.Gravity;
@ -19,10 +20,13 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.TextView;
import androidx.dynamicanimation.animation.FloatValueHolder;
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.ContactsController;
import org.telegram.messenger.DialogObject;
import org.telegram.messenger.ImageLocation;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.MessagesController;
import org.telegram.messenger.R;
@ -30,16 +34,19 @@ import org.telegram.messenger.UserConfig;
import org.telegram.messenger.UserObject;
import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.SimpleTextView;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Components.AvatarDrawable;
import org.telegram.ui.Components.BackupImageView;
import org.telegram.ui.Components.CheckBox2;
import org.telegram.ui.Components.Forum.ForumUtilities;
import org.telegram.ui.Components.LayoutHelper;
public class ShareDialogCell extends FrameLayout {
private BackupImageView imageView;
private TextView nameTextView;
private SimpleTextView topicTextView;
private CheckBox2 checkBox;
private AvatarDrawable avatarDrawable = new AvatarDrawable();
private TLRPC.User user;
@ -49,6 +56,8 @@ public class ShareDialogCell extends FrameLayout {
private long lastUpdateTime;
private long currentDialog;
private boolean topicWasVisible;
private int currentAccount = UserConfig.selectedAccount;
private final Theme.ResourcesProvider resourcesProvider;
@ -80,6 +89,14 @@ public class ShareDialogCell extends FrameLayout {
nameTextView.setEllipsize(TextUtils.TruncateAt.END);
addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 6, currentType == TYPE_CREATE ? 58 : 66, 6, 0));
topicTextView = new SimpleTextView(context);
topicTextView.setTextColor(getThemedColor(type == TYPE_CALL ? Theme.key_voipgroup_nameText : Theme.key_dialogTextBlack));
topicTextView.setTextSize(12);
topicTextView.setMaxLines(2);
topicTextView.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL);
topicTextView.setAlignment(Layout.Alignment.ALIGN_CENTER);
addView(topicTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 6, currentType == TYPE_CREATE ? 58 : 66, 6, 0));
checkBox = new CheckBox2(context, 21, resourcesProvider);
checkBox.setColor(Theme.key_dialogRoundCheckBox, type == TYPE_CALL ? Theme.key_voipgroup_inviteMembersBackground : Theme.key_dialogBackground, Theme.key_dialogRoundCheckBoxCheck);
checkBox.setDrawUnchecked(false);
@ -122,6 +139,7 @@ public class ShareDialogCell extends FrameLayout {
}
imageView.setForUserOrChat(user, avatarDrawable);
}
imageView.setRoundRadius(AndroidUtilities.dp(28));
} else {
user = null;
TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-uid);
@ -134,6 +152,7 @@ public class ShareDialogCell extends FrameLayout {
}
avatarDrawable.setInfo(chat);
imageView.setForUserOrChat(chat, avatarDrawable);
imageView.setRoundRadius(chat != null && chat.forum ? AndroidUtilities.dp(16) : AndroidUtilities.dp(28));
}
currentDialog = uid;
checkBox.setChecked(checked, false);
@ -145,6 +164,59 @@ public class ShareDialogCell extends FrameLayout {
public void setChecked(boolean checked, boolean animated) {
checkBox.setChecked(checked, animated);
if (!checked) {
setTopic(null, true);
}
}
public void setTopic(TLRPC.TL_forumTopic topic, boolean animate) {
boolean wasVisible = topicWasVisible;
boolean visible = topic != null;
if (wasVisible != visible || !animate) {
SpringAnimation prevSpring = (SpringAnimation) topicTextView.getTag(R.id.spring_tag);
if (prevSpring != null) {
prevSpring.cancel();
}
if (visible) {
topicTextView.setText(ForumUtilities.getTopicSpannedName(topic, topicTextView.getTextPaint()));
topicTextView.requestLayout();
}
if (animate) {
SpringAnimation springAnimation = new SpringAnimation(new FloatValueHolder(visible ? 0f : 1000f))
.setSpring(new SpringForce(visible ? 1000f : 0f)
.setStiffness(SpringForce.STIFFNESS_MEDIUM)
.setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY))
.addUpdateListener((animation, value, velocity) -> {
value /= 1000f;
topicTextView.setAlpha(value);
nameTextView.setAlpha(1f - value);
topicTextView.setTranslationX((1f - value) * -AndroidUtilities.dp(10));
nameTextView.setTranslationX(value * AndroidUtilities.dp(10));
})
.addEndListener((animation, canceled, value, velocity) -> {
topicTextView.setTag(R.id.spring_tag, null);
});
topicTextView.setTag(R.id.spring_tag, springAnimation);
springAnimation.start();
} else {
if (visible) {
topicTextView.setAlpha(1f);
nameTextView.setAlpha(0f);
topicTextView.setTranslationX(0);
nameTextView.setTranslationX(AndroidUtilities.dp(10));
} else {
topicTextView.setAlpha(0f);
nameTextView.setAlpha(1f);
topicTextView.setTranslationX(-AndroidUtilities.dp(10));
nameTextView.setTranslationX(0);
}
}
topicWasVisible = visible;
}
}
@Override
@ -198,7 +270,9 @@ public class ShareDialogCell extends FrameLayout {
int cy = imageView.getTop() + imageView.getMeasuredHeight() / 2;
Theme.checkboxSquare_checkPaint.setColor(getThemedColor(Theme.key_dialogRoundCheckBox));
Theme.checkboxSquare_checkPaint.setAlpha((int) (checkBox.getProgress() * 255));
canvas.drawCircle(cx, cy, AndroidUtilities.dp(currentType == TYPE_CREATE ? 24 : 28), Theme.checkboxSquare_checkPaint);
int radius = AndroidUtilities.dp(currentType == TYPE_CREATE ? 24 : 28);
AndroidUtilities.rectTmp.set(cx - radius, cy - radius, cx + radius, cy + radius);
canvas.drawRoundRect(AndroidUtilities.rectTmp, imageView.getRoundRadius()[0], imageView.getRoundRadius()[0], Theme.checkboxSquare_checkPaint);
super.onDraw(canvas);
}

View File

@ -0,0 +1,109 @@
/*
* This is the source code of Telegram for Android v. 5.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-2018.
*/
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.TextView;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.MessagesController;
import org.telegram.messenger.UserConfig;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Components.AnimatedEmojiDrawable;
import org.telegram.ui.Components.BackupImageView;
import org.telegram.ui.Components.CombinedDrawable;
import org.telegram.ui.Components.Forum.ForumBubbleDrawable;
import org.telegram.ui.Components.LayoutHelper;
import org.telegram.ui.Components.LetterDrawable;
public class ShareTopicCell extends FrameLayout {
private BackupImageView imageView;
private TextView nameTextView;
private long currentDialog;
private long currentTopic;
private int currentAccount = UserConfig.selectedAccount;
private final Theme.ResourcesProvider resourcesProvider;
public ShareTopicCell(Context context, Theme.ResourcesProvider resourcesProvider) {
super(context);
this.resourcesProvider = resourcesProvider;
setWillNotDraw(false);
imageView = new BackupImageView(context);
imageView.setRoundRadius(AndroidUtilities.dp(28));
addView(imageView, LayoutHelper.createFrame(56, 56, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 7, 0, 0));
nameTextView = new TextView(context);
nameTextView.setTextColor(getThemedColor(Theme.key_dialogTextBlack));
nameTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12);
nameTextView.setMaxLines(2);
nameTextView.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL);
nameTextView.setLines(2);
nameTextView.setEllipsize(TextUtils.TruncateAt.END);
addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 6, 66, 6, 0));
setBackground(Theme.createRadSelectorDrawable(Theme.getColor(Theme.key_listSelector), AndroidUtilities.dp(2), AndroidUtilities.dp(2)));
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(103), MeasureSpec.EXACTLY));
}
public void setTopic(TLRPC.Dialog dialog, TLRPC.TL_forumTopic topic, boolean checked, CharSequence name) {
TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-dialog.id);
if (name != null) {
nameTextView.setText(name);
} else if (chat != null) {
nameTextView.setText(topic.title);
} else {
nameTextView.setText("");
}
if (topic.icon_emoji_id != 0) {
imageView.setImageDrawable(null);
imageView.setAnimatedEmojiDrawable(new AnimatedEmojiDrawable(AnimatedEmojiDrawable.CACHE_TYPE_FORUM_TOPIC, UserConfig.selectedAccount, topic.icon_emoji_id));
} else {
imageView.setAnimatedEmojiDrawable(null);
ForumBubbleDrawable forumBubbleDrawable = new ForumBubbleDrawable(topic.icon_color);
LetterDrawable letterDrawable = new LetterDrawable(null, LetterDrawable.STYLE_TOPIC_DRAWABLE);
String title = topic.title.trim().toUpperCase();
letterDrawable.setTitle(title.length() >= 1 ? title.substring(0, 1) : "");
letterDrawable.scale = 1.8f;
CombinedDrawable combinedDrawable = new CombinedDrawable(forumBubbleDrawable, letterDrawable, 0, 0);
combinedDrawable.setFullsize(true);
imageView.setImageDrawable(combinedDrawable);
}
imageView.setRoundRadius(chat != null && chat.forum && !checked ? AndroidUtilities.dp(16) : AndroidUtilities.dp(28));
currentDialog = dialog.id;
currentTopic = topic.id;
}
public long getCurrentDialog() {
return currentDialog;
}
public long getCurrentTopic() {
return currentTopic;
}
private int getThemedColor(String key) {
Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null;
return color != null ? color : Theme.getColor(key);
}
}

View File

@ -205,7 +205,7 @@ public class SharedLinkCell extends FrameLayout {
setWillNotDraw(false);
linkImageView = new ImageReceiver(this);
linkImageView.setRoundRadius(AndroidUtilities.dp(4));
letterDrawable = new LetterDrawable(resourcesProvider);
letterDrawable = new LetterDrawable(resourcesProvider, LetterDrawable.STYLE_DEFAULT);
checkBox = new CheckBox2(context, 21, resourcesProvider);
checkBox.setVisibility(INVISIBLE);

View File

@ -314,7 +314,7 @@ public class StickerEmojiCell extends FrameLayout implements NotificationCenter.
if (v != null) {
v.vibrate(200);
}
AndroidUtilities.shakeView(premiumIconView, 2, 0);
AndroidUtilities.shakeView(premiumIconView);
}
}

View File

@ -23,15 +23,18 @@ import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.LocaleController;
import org.telegram.ui.ActionBar.SimpleTextView;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Components.AnimatedTextView;
import org.telegram.ui.Components.LayoutHelper;
import org.telegram.ui.Components.RLottieDrawable;
import org.telegram.ui.Components.RLottieImageView;
import org.telegram.ui.Components.Switch;
public class TextCell extends FrameLayout {
public final SimpleTextView textView;
public final SimpleTextView valueTextView;
public final AnimatedTextView valueTextView;
public final RLottieImageView imageView;
private Switch checkBox;
private ImageView valueImageView;
private int leftPadding;
private boolean needDivider;
@ -40,20 +43,22 @@ public class TextCell extends FrameLayout {
private boolean inDialogs;
private boolean prioritizeTitleOverValue;
private Theme.ResourcesProvider resourcesProvider;
private boolean attached;
public TextCell(Context context) {
this(context, 23, false, null);
this(context, 23, false, false, null);
}
public TextCell(Context context, Theme.ResourcesProvider resourcesProvider) {
this(context, 23, false, resourcesProvider);
this(context, 23, false, false, resourcesProvider);
}
public TextCell(Context context, int left, boolean dialog) {
this(context, left, dialog, null);
this(context, left, dialog, false, null);
}
public TextCell(Context context, int left, boolean dialog, Theme.ResourcesProvider resourcesProvider) {
public TextCell(Context context, int left, boolean dialog, boolean needCheck, Theme.ResourcesProvider resourcesProvider) {
super(context);
this.resourcesProvider = resourcesProvider;
@ -66,9 +71,10 @@ public class TextCell extends FrameLayout {
textView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT));
valueTextView = new SimpleTextView(context);
valueTextView = new AnimatedTextView(context);
valueTextView.setTextColor(Theme.getColor(dialog ? Theme.key_dialogTextBlue2 : Theme.key_windowBackgroundWhiteValueText, resourcesProvider));
valueTextView.setTextSize(16);
valueTextView.setPadding(0, AndroidUtilities.dp(18), 0, AndroidUtilities.dp(18));
valueTextView.setTextSize(AndroidUtilities.dp(16));
valueTextView.setGravity(LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT);
valueTextView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
addView(valueTextView);
@ -82,9 +88,19 @@ public class TextCell extends FrameLayout {
valueImageView.setScaleType(ImageView.ScaleType.CENTER);
addView(valueImageView);
if (needCheck) {
checkBox = new Switch(context, resourcesProvider);
checkBox.setColors(Theme.key_switchTrack, Theme.key_switchTrackChecked, Theme.key_windowBackgroundWhite, Theme.key_windowBackgroundWhite);
addView(checkBox, LayoutHelper.createFrame(37, 20, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL, 22, 0, 22, 0));
}
setFocusable(true);
}
public Switch getCheckBox() {
return checkBox;
}
public void setIsInDialogs() {
inDialogs = true;
}
@ -97,7 +113,7 @@ public class TextCell extends FrameLayout {
return imageView;
}
public SimpleTextView getValueTextView() {
public AnimatedTextView getValueTextView() {
return valueTextView;
}
@ -117,10 +133,10 @@ public class TextCell extends FrameLayout {
if (prioritizeTitleOverValue) {
textView.measure(MeasureSpec.makeMeasureSpec(width - AndroidUtilities.dp(71 + leftPadding), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(20), MeasureSpec.EXACTLY));
valueTextView.measure(MeasureSpec.makeMeasureSpec(width - AndroidUtilities.dp(103 + leftPadding) - textView.getTextWidth(), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(20), MeasureSpec.EXACTLY));
valueTextView.measure(MeasureSpec.makeMeasureSpec(width - AndroidUtilities.dp(103 + leftPadding) - textView.getTextWidth(), LocaleController.isRTL ? MeasureSpec.AT_MOST : MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(20), MeasureSpec.EXACTLY));
} else {
valueTextView.measure(MeasureSpec.makeMeasureSpec(width - AndroidUtilities.dp(leftPadding), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(20), MeasureSpec.EXACTLY));
textView.measure(MeasureSpec.makeMeasureSpec(width - AndroidUtilities.dp(71 + leftPadding) - valueTextView.getTextWidth(), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(20), MeasureSpec.EXACTLY));
valueTextView.measure(MeasureSpec.makeMeasureSpec(width - AndroidUtilities.dp(leftPadding), LocaleController.isRTL ? MeasureSpec.AT_MOST : MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(20), MeasureSpec.EXACTLY));
textView.measure(MeasureSpec.makeMeasureSpec(width - AndroidUtilities.dp(71 + leftPadding) - valueTextView.width(), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(20), MeasureSpec.EXACTLY));
}
if (imageView.getVisibility() == VISIBLE) {
imageView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
@ -128,9 +144,20 @@ public class TextCell extends FrameLayout {
if (valueImageView.getVisibility() == VISIBLE) {
valueImageView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
}
if (checkBox != null) {
checkBox.measure(MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(37), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(20), MeasureSpec.EXACTLY));
}
setMeasuredDimension(width, AndroidUtilities.dp(50) + (needDivider ? 1 : 0));
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
if (checkBox != null) {
checkBox.setEnabled(enabled);
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int height = bottom - top;
@ -162,6 +189,11 @@ public class TextCell extends FrameLayout {
viewLeft = LocaleController.isRTL ? AndroidUtilities.dp(23) : width - valueImageView.getMeasuredWidth() - AndroidUtilities.dp(23);
valueImageView.layout(viewLeft, viewTop, viewLeft + valueImageView.getMeasuredWidth(), viewTop + valueImageView.getMeasuredHeight());
}
if (checkBox != null && checkBox.getVisibility() == VISIBLE) {
viewTop = (height - checkBox.getMeasuredHeight()) / 2;
viewLeft = LocaleController.isRTL ? AndroidUtilities.dp(22) : width - checkBox.getMeasuredWidth() - AndroidUtilities.dp(22);
checkBox.layout(viewLeft, viewTop, viewLeft + checkBox.getMeasuredWidth(), viewTop + checkBox.getMeasuredHeight());
}
}
public void setTextColor(int color) {
@ -180,7 +212,7 @@ public class TextCell extends FrameLayout {
public void setText(String text, boolean divider) {
imageLeft = 21;
textView.setText(text);
valueTextView.setText(null);
valueTextView.setText(null, false);
imageView.setVisibility(GONE);
valueTextView.setVisibility(GONE);
valueImageView.setVisibility(GONE);
@ -192,7 +224,7 @@ public class TextCell extends FrameLayout {
imageLeft = 21;
offsetFromImage = 71;
textView.setText(text);
valueTextView.setText(null);
valueTextView.setText(null, false);
imageView.setImageResource(resId);
imageView.setVisibility(VISIBLE);
valueTextView.setVisibility(GONE);
@ -206,7 +238,7 @@ public class TextCell extends FrameLayout {
offsetFromImage = 68;
imageLeft = 18;
textView.setText(text);
valueTextView.setText(null);
valueTextView.setText(null, false);
imageView.setColorFilter(null);
if (drawable instanceof RLottieDrawable) {
imageView.setAnimation((RLottieDrawable) drawable);
@ -230,22 +262,33 @@ public class TextCell extends FrameLayout {
}
public void setTextAndValue(String text, String value, boolean divider) {
setTextAndValue(text, value, false, divider);
}
public void setTextAndValue(String text, String value, boolean animated, boolean divider) {
imageLeft = 21;
offsetFromImage = 71;
textView.setText(text);
valueTextView.setText(value);
valueTextView.setText(value, animated);
valueTextView.setVisibility(VISIBLE);
imageView.setVisibility(GONE);
valueImageView.setVisibility(GONE);
needDivider = divider;
setWillNotDraw(!needDivider);
if (checkBox != null) {
checkBox.setVisibility(GONE);
}
}
public void setTextAndValueAndIcon(String text, String value, int resId, boolean divider) {
setTextAndValueAndIcon(text, value, false, resId, divider);
}
public void setTextAndValueAndIcon(String text, String value, boolean animated, int resId, boolean divider) {
imageLeft = 21;
offsetFromImage = 71;
textView.setText(text);
valueTextView.setText(value);
valueTextView.setText(value, animated);
valueTextView.setVisibility(VISIBLE);
valueImageView.setVisibility(GONE);
imageView.setVisibility(VISIBLE);
@ -253,13 +296,33 @@ public class TextCell extends FrameLayout {
imageView.setImageResource(resId);
needDivider = divider;
setWillNotDraw(!needDivider);
if (checkBox != null) {
checkBox.setVisibility(GONE);
}
}
public void setTextAndCheckAndIcon(String text, boolean checked, int resId, boolean divider) {
imageLeft = 21;
offsetFromImage = 71;
textView.setText(text);
valueTextView.setVisibility(GONE);
valueImageView.setVisibility(GONE);
if (checkBox != null) {
checkBox.setVisibility(VISIBLE);
checkBox.setChecked(checked, false);
}
imageView.setVisibility(VISIBLE);
imageView.setPadding(0, AndroidUtilities.dp(7), 0, 0);
imageView.setImageResource(resId);
needDivider = divider;
setWillNotDraw(!needDivider);
}
public void setTextAndValueDrawable(String text, Drawable drawable, boolean divider) {
imageLeft = 21;
offsetFromImage = 71;
textView.setText(text);
valueTextView.setText(null);
valueTextView.setText(null, false);
valueImageView.setVisibility(VISIBLE);
valueImageView.setImageDrawable(drawable);
valueTextView.setVisibility(GONE);
@ -267,6 +330,9 @@ public class TextCell extends FrameLayout {
imageView.setPadding(0, AndroidUtilities.dp(7), 0, 0);
needDivider = divider;
setWillNotDraw(!needDivider);
if (checkBox != null) {
checkBox.setVisibility(GONE);
}
}
@Override
@ -298,4 +364,51 @@ public class TextCell extends FrameLayout {
invalidate();
}
}
public void setChecked(boolean checked) {
checkBox.setChecked(checked, true);
}
public void showEnabledAlpha(boolean show) {
float alpha = show ? 0.5f : 1f;
if (attached) {
if (imageView != null) {
imageView.animate().alpha(alpha).start();
}
if (textView != null) {
textView.animate().alpha(alpha).start();
}
if (valueTextView != null) {
valueTextView.animate().alpha(alpha).start();
}
if (valueImageView != null) {
valueImageView.animate().alpha(alpha).start();
}
} else {
if (imageView != null) {
imageView.setAlpha(alpha);
}
if (textView != null) {
textView.setAlpha(alpha);
}
if (valueTextView != null) {
valueTextView.setAlpha(alpha);
}
if (valueImageView != null) {
valueImageView.setAlpha(alpha);
}
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
attached = true;
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
attached = false;
}
}

View File

@ -28,7 +28,6 @@ import android.widget.TextView;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.R;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Components.AnimationProperties;
import org.telegram.ui.Components.CubicBezierInterpolator;
@ -144,7 +143,7 @@ public class TextCheckCell extends FrameLayout {
public void setTextAndCheck(String text, boolean checked, boolean divider) {
textView.setText(text);
isMultiline = false;
checkBox.setChecked(checked, false);
checkBox.setChecked(checked, attached);
needDivider = divider;
valueTextView.setVisibility(GONE);
LayoutParams layoutParams = (LayoutParams) textView.getLayoutParams();
@ -340,4 +339,18 @@ public class TextCheckCell extends FrameLayout {
}
info.setContentDescription(sb);
}
boolean attached;
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
attached = true;
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
attached = false;
}
}

View File

@ -33,10 +33,14 @@ public class TextCheckCell2 extends FrameLayout {
private boolean isMultiline;
public TextCheckCell2(Context context) {
this(context, null);
}
public TextCheckCell2(Context context, Theme.ResourcesProvider resourcesProvider) {
super(context);
textView = new TextView(context);
textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText));
textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider));
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
textView.setLines(1);
textView.setMaxLines(1);
@ -46,7 +50,7 @@ public class TextCheckCell2 extends FrameLayout {
addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 64 : 21, 0, LocaleController.isRTL ? 21 : 64, 0));
valueTextView = new TextView(context);
valueTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2));
valueTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2, resourcesProvider));
valueTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13);
valueTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT);
valueTextView.setLines(1);

View File

@ -13,8 +13,11 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
@ -24,17 +27,20 @@ import android.widget.TextView;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.Emoji;
import org.telegram.messenger.LocaleController;
import org.telegram.ui.ActionBar.SimpleTextView;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Components.LayoutHelper;
import org.telegram.ui.Components.LinkSpanDrawable;
public class TextDetailCell extends FrameLayout {
private final TextView textView;
private final TextView valueTextView;
private final LinkSpanDrawable.LinksTextView valueTextView;
private final TextView showMoreTextView = null;
private final ImageView imageView;
private boolean needDivider;
private boolean contentDescriptionValueFirst;
private boolean multiline;
private Theme.ResourcesProvider resourcesProvider;
@ -43,6 +49,10 @@ public class TextDetailCell extends FrameLayout {
}
public TextDetailCell(Context context, Theme.ResourcesProvider resourcesProvider) {
this(context, resourcesProvider, false);
}
public TextDetailCell(Context context, Theme.ResourcesProvider resourcesProvider, boolean multiline) {
super(context);
this.resourcesProvider = resourcesProvider;
@ -57,15 +67,27 @@ public class TextDetailCell extends FrameLayout {
textView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT, 23, 8, 23, 0));
valueTextView = new TextView(context);
valueTextView = new LinkSpanDrawable.LinksTextView(context);
valueTextView.setOnLinkLongPressListener(span -> {
if (span != null) {
try {
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
} catch (Exception ignore) {};
span.onClick(valueTextView);
}
});
if (this.multiline = multiline) {
setMinimumHeight(AndroidUtilities.dp(60));
} else {
valueTextView.setLines(1);
valueTextView.setSingleLine(true);
}
valueTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2, resourcesProvider));
valueTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13);
valueTextView.setLines(1);
valueTextView.setMaxLines(1);
valueTextView.setSingleLine(true);
valueTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT);
valueTextView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
addView(valueTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT, 23, 33, 23, 0));
valueTextView.setEllipsize(TextUtils.TruncateAt.END);
addView(valueTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT, 23, 33, 23, 10));
imageView = new ImageView(context);
imageView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
@ -73,15 +95,24 @@ public class TextDetailCell extends FrameLayout {
addView(imageView, LayoutHelper.createFrameRelatively(48, 48, Gravity.END | Gravity.CENTER_VERTICAL, 0, 0, 12, 0));
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean hit = valueTextView.hit((int) ev.getX() - valueTextView.getLeft(), (int) ev.getY() - valueTextView.getTop()) != null;
if (hit) {
return true;
}
return super.onTouchEvent(ev);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(
MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(60) + (needDivider ? 1 : 0), MeasureSpec.EXACTLY)
multiline ? heightMeasureSpec : MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(60) + (needDivider ? 1 : 0), MeasureSpec.EXACTLY)
);
}
public void setTextAndValue(String text, String value, boolean divider) {
public void setTextAndValue(CharSequence text, CharSequence value, boolean divider) {
textView.setText(text);
valueTextView.setText(value);
needDivider = divider;
@ -93,6 +124,7 @@ public class TextDetailCell extends FrameLayout {
}
public void setImage(Drawable drawable, CharSequence imageContentDescription) {
((MarginLayoutParams) valueTextView.getLayoutParams()).rightMargin = !LocaleController.isRTL && drawable != null ? AndroidUtilities.dp(28 + 12 + 12 + 6) : AndroidUtilities.dp(23);
imageView.setImageDrawable(drawable);
imageView.setFocusable(drawable != null);
imageView.setContentDescription(imageContentDescription);

View File

@ -25,8 +25,8 @@ import org.telegram.messenger.MessageObject;
import org.telegram.messenger.R;
import org.telegram.messenger.UserConfig;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.ActionBarLayout;
import org.telegram.ui.ActionBar.BaseFragment;
import org.telegram.ui.ActionBar.INavigationLayout;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Components.BackgroundGradientDrawable;
import org.telegram.ui.Components.LayoutHelper;
@ -47,13 +47,13 @@ public class ThemePreviewMessagesCell extends LinearLayout {
private Drawable oldBackgroundDrawable;
private ChatMessageCell[] cells = new ChatMessageCell[2];
private Drawable shadowDrawable;
private ActionBarLayout parentLayout;
private INavigationLayout parentLayout;
private final int type;
public BaseFragment fragment;
@SuppressLint("ClickableViewAccessibility")
public ThemePreviewMessagesCell(Context context, ActionBarLayout layout, int type) {
public ThemePreviewMessagesCell(Context context, INavigationLayout layout, int type) {
super(context);
this.type = type;
int currentAccount = UserConfig.selectedAccount;

View File

@ -0,0 +1,64 @@
package org.telegram.ui.Cells;
import android.content.Context;
import android.graphics.Canvas;
import android.util.TypedValue;
import android.view.Gravity;
import android.widget.FrameLayout;
import android.widget.TextView;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.MessagesController;
import org.telegram.messenger.UserConfig;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Components.BackupImageView;
import org.telegram.ui.Components.Forum.ForumUtilities;
import org.telegram.ui.Components.LayoutHelper;
public class TopicExceptionCell extends FrameLayout {
public boolean drawDivider;
BackupImageView backupImageView;
TextView title;
TextView subtitle;
public TopicExceptionCell(Context context) {
super(context);
backupImageView = new BackupImageView(context);
addView(backupImageView, LayoutHelper.createFrame(30, 30, Gravity.CENTER_VERTICAL, 20, 0, 0, 0));
title = new TextView(context);
title.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText));
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
title.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
title.setMaxLines(1);
addView(title, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 72, 8, 12, 0));
subtitle = new TextView(context);
subtitle.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText));
subtitle.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
addView(subtitle, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 72, 32, 12, 0));
}
public void setTopic(long dialogId, TLRPC.TL_forumTopic topic) {
ForumUtilities.setTopicIcon(backupImageView, topic);
title.setText(topic.title);
subtitle.setText(MessagesController.getInstance(UserConfig.selectedAccount).getMutedString(dialogId, topic.id));
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(56), MeasureSpec.EXACTLY));
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (drawDivider) {
canvas.drawLine(AndroidUtilities.dp(72), getMeasuredHeight() - 1, getMeasuredWidth(), getMeasuredHeight() - 1, Theme.dividerPaint);
}
}
}

View File

@ -0,0 +1,78 @@
package org.telegram.ui.Cells;
import android.content.Context;
import android.graphics.Canvas;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.Gravity;
import android.widget.FrameLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.LocaleController;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Components.BackupImageView;
import org.telegram.ui.Components.Forum.ForumUtilities;
import org.telegram.ui.Components.LayoutHelper;
public class TopicSearchCell extends FrameLayout {
BackupImageView backupImageView;
TextView textView;
TLRPC.TL_forumTopic topic;
public boolean drawDivider;
public TopicSearchCell(@NonNull Context context) {
super(context);
backupImageView = new BackupImageView(context);
textView = new TextView(context);
textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText));
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
if (LocaleController.isRTL) {
addView(backupImageView, LayoutHelper.createFrame(30, 30, Gravity.CENTER_VERTICAL | Gravity.RIGHT, 12, 0, 12, 0));
addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL | Gravity.RIGHT, 12, 0, 56, 0));
} else {
addView(backupImageView, LayoutHelper.createFrame(30, 30, Gravity.CENTER_VERTICAL, 12, 0, 12, 0));
addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL, 56, 0, 12, 0));
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(48), MeasureSpec.EXACTLY));
}
public void setTopic(TLRPC.TL_forumTopic topic) {
this.topic = topic;
if (TextUtils.isEmpty(topic.searchQuery)) {
textView.setText(topic.title);
} else {
textView.setText(AndroidUtilities.highlightText(topic.title, topic.searchQuery, null));
}
ForumUtilities.setTopicIcon(backupImageView, topic);
}
public TLRPC.TL_forumTopic getTopic() {
return topic;
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (drawDivider) {
int left = AndroidUtilities.dp(56);
if (LocaleController.isRTL) {
canvas.drawLine(0, getMeasuredHeight() - 1, getMeasuredWidth() - left, getMeasuredHeight() - 1, Theme.dividerPaint);
} else {
canvas.drawLine(left, getMeasuredHeight() - 1, getMeasuredWidth(), getMeasuredHeight() - 1, Theme.dividerPaint);
}
}
}
}

View File

@ -275,7 +275,7 @@ public class UserCell2 extends FrameLayout {
if (ChatObject.isChannel(currentChat) && !currentChat.megagroup) {
if (currentChat.participants_count != 0) {
statusTextView.setText(LocaleController.formatPluralString("Subscribers", currentChat.participants_count));
} else if (TextUtils.isEmpty(currentChat.username)) {
} else if (!ChatObject.isPublic(currentChat)) {
statusTextView.setText(LocaleController.getString("ChannelPrivate", R.string.ChannelPrivate));
} else {
statusTextView.setText(LocaleController.getString("ChannelPublic", R.string.ChannelPublic));
@ -285,7 +285,7 @@ public class UserCell2 extends FrameLayout {
statusTextView.setText(LocaleController.formatPluralString("Members", currentChat.participants_count));
} else if (currentChat.has_geo) {
statusTextView.setText(LocaleController.getString("MegaLocation", R.string.MegaLocation));
} else if (TextUtils.isEmpty(currentChat.username)) {
} else if (!ChatObject.isPublic(currentChat)) {
statusTextView.setText(LocaleController.getString("MegaPrivate", R.string.MegaPrivate));
} else {
statusTextView.setText(LocaleController.getString("MegaPublic", R.string.MegaPublic));

View File

@ -120,7 +120,7 @@ public class ChangeBioActivity extends BaseFragment {
if (v != null) {
v.vibrate(200);
}
AndroidUtilities.shakeView(checkTextView, 2, 0);
AndroidUtilities.shakeView(checkTextView);
}
return result;
}

View File

@ -1097,7 +1097,7 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio
AndroidUtilities.updateViewVisibilityAnimated(progressView, false, 0.3f, true);
chatListView.setEmptyView(emptyViewContainer);
}
chatListView.setAnimateEmptyView(true, 1);
chatListView.setAnimateEmptyView(true, RecyclerListView.EMPTY_VIEW_ANIMATION_TYPE_ALPHA_SCALE);
undoView = new UndoView(context);
undoView.setAdditionalTranslationY(AndroidUtilities.dp(51));
@ -1128,7 +1128,7 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio
ArrayList<CharSequence> items = new ArrayList<>();
final ArrayList<Integer> options = new ArrayList<>();
if (selectedObject.type == 0 || selectedObject.caption != null) {
if (selectedObject.type == MessageObject.TYPE_TEXT || selectedObject.caption != null) {
items.add(LocaleController.getString("Copy", R.string.Copy));
options.add(3);
}
@ -1385,13 +1385,13 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio
if (path == null || path.length() == 0) {
path = getFileLoader().getPathToMessage(selectedObject.messageOwner).toString();
}
if (selectedObject.type == 3 || selectedObject.type == 1) {
if (selectedObject.type == MessageObject.TYPE_VIDEO || selectedObject.type == MessageObject.TYPE_PHOTO) {
if (Build.VERSION.SDK_INT >= 23 && (Build.VERSION.SDK_INT <= 28 || BuildVars.NO_SCOPED_STORAGE) && getParentActivity().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
getParentActivity().requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 4);
selectedObject = null;
return;
}
MediaController.saveFile(path, getParentActivity(), selectedObject.type == 3 ? 1 : 0, null, null);
MediaController.saveFile(path, getParentActivity(), selectedObject.type == MessageObject.TYPE_VIDEO ? 1 : 0, null, null);
}
break;
}
@ -1571,7 +1571,7 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio
}
if (messageObject.type == 6) {
return -1;
} else if (messageObject.type == 10 || messageObject.type == 11 || messageObject.type == 16) {
} else if (messageObject.type == 10 || messageObject.type == MessageObject.TYPE_ACTION_PHOTO || messageObject.type == MessageObject.TYPE_PHONE_CALL) {
if (messageObject.getId() == 0) {
return -1;
}
@ -1619,7 +1619,7 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio
}
return 4;
}
} else if (messageObject.type == 12) {
} else if (messageObject.type == MessageObject.TYPE_CONTACT) {
return 8;
} else if (messageObject.isMediaEmpty()) {
return 3;
@ -1649,7 +1649,7 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio
}
@Override
protected void onRemoveFromParent() {
public void onRemoveFromParent() {
MediaController.getInstance().setTextureView(videoTextureView, null, null, false);
}
@ -1910,7 +1910,7 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio
}
@Override
protected void onBecomeFullyHidden() {
public void onBecomeFullyHidden() {
if (undoView != null) {
undoView.hide(true, 0);
}
@ -1944,7 +1944,7 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio
AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity());
builder.setTitle(LocaleController.getString("AppName", R.string.AppName));
builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null);
if (message.type == 3) {
if (message.type == MessageObject.TYPE_VIDEO) {
builder.setMessage(LocaleController.getString("NoPlayerInstalled", R.string.NoPlayerInstalled));
} else {
builder.setMessage(LocaleController.formatString("NoHandleAppInstalled", R.string.NoHandleAppInstalled, message.getDocument().mime_type));
@ -2063,9 +2063,9 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio
}
@Override
public boolean needPlayMessage(MessageObject messageObject) {
public boolean needPlayMessage(MessageObject messageObject, boolean muted) {
if (messageObject.isVoice() || messageObject.isRoundVideo()) {
boolean result = MediaController.getInstance().playMessage(messageObject);
boolean result = MediaController.getInstance().playMessage(messageObject, muted);
MediaController.getInstance().setVoiceMessagesPlaylist(null, false);
return result;
} else if (messageObject.isMusic()) {
@ -2253,10 +2253,10 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio
MessageObject message = cell.getMessageObject();
if (message.getInputStickerSet() != null) {
showDialog(new StickersAlert(getParentActivity(), ChannelAdminLogActivity.this, message.getInputStickerSet(), null, null));
} else if (message.isVideo() || message.type == 1 || message.type == 0 && !message.isWebpageDocument() || message.isGif()) {
} else if (message.isVideo() || message.type == MessageObject.TYPE_PHOTO || message.type == MessageObject.TYPE_TEXT && !message.isWebpageDocument() || message.isGif()) {
PhotoViewer.getInstance().setParentActivity(ChannelAdminLogActivity.this);
PhotoViewer.getInstance().openPhoto(message, null, 0, 0, provider);
} else if (message.type == 3) {
PhotoViewer.getInstance().openPhoto(message, null, 0, 0, 0, provider);
} else if (message.type == MessageObject.TYPE_VIDEO) {
try {
File f = null;
if (message.messageOwner.attachPath != null && message.messageOwner.attachPath.length() != 0) {
@ -2276,14 +2276,14 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio
} catch (Exception e) {
alertUserOpenError(message);
}
} else if (message.type == 4) {
} else if (message.type == MessageObject.TYPE_GEO) {
if (!AndroidUtilities.isMapsInstalled(ChannelAdminLogActivity.this)) {
return;
}
LocationActivity fragment = new LocationActivity(0);
fragment.setMessageObject(message);
presentFragment(fragment);
} else if (message.type == 9 || message.type == 0) {
} else if (message.type == MessageObject.TYPE_FILE || message.type == MessageObject.TYPE_TEXT) {
if (message.getDocumentName().toLowerCase().endsWith("attheme")) {
File locFile = null;
if (message.messageOwner.attachPath != null && message.messageOwner.attachPath.length() != 0) {
@ -2366,7 +2366,7 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio
ImageLocation imageLocation = ImageLocation.getForPhoto(photoSize, message.messageOwner.action.photo);
PhotoViewer.getInstance().openPhoto(photoSize.location, imageLocation, provider);
} else {
PhotoViewer.getInstance().openPhoto(message, null, 0, 0, provider);
PhotoViewer.getInstance().openPhoto(message, null, 0, 0, 0, provider);
}
}
@ -2450,6 +2450,16 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio
}
@Override
public BaseFragment getBaseFragment() {
return ChannelAdminLogActivity.this;
}
@Override
public long getDialogId() {
return -currentChat.id;
}
@Override
public void didPressReplyMessage(ChatActionCell cell, int id) {

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