Update to 5.6.1
|
@ -10,23 +10,26 @@ configurations {
|
||||||
compile.exclude module: 'support-v4'
|
compile.exclude module: 'support-v4'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configurations.all {
|
||||||
|
exclude group: 'com.google.firebase', module: 'firebase-core'
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation 'androidx.core:core:1.1.0-beta01'
|
||||||
|
implementation 'androidx.palette:palette:1.0.0'
|
||||||
|
implementation 'androidx.exifinterface:exifinterface:1.0.0'
|
||||||
|
|
||||||
|
implementation 'com.airbnb.android:lottie:3.0.1'
|
||||||
|
|
||||||
compileOnly 'org.checkerframework:checker-qual:2.5.2'
|
compileOnly 'org.checkerframework:checker-qual:2.5.2'
|
||||||
compileOnly 'org.checkerframework:checker-compat-qual:2.5.0'
|
compileOnly 'org.checkerframework:checker-compat-qual:2.5.0'
|
||||||
implementation 'com.google.firebase:firebase-core:16.0.7'
|
implementation 'com.google.firebase:firebase-messaging:18.0.0'
|
||||||
implementation 'com.google.firebase:firebase-messaging:17.3.4'
|
implementation 'com.google.firebase:firebase-config:16.5.0'
|
||||||
implementation 'com.google.firebase:firebase-config:16.1.3'
|
implementation 'com.google.android.gms:play-services-maps:16.1.0'
|
||||||
implementation 'com.google.android.gms:play-services-maps:16.0.0'
|
|
||||||
implementation 'com.google.android.gms:play-services-auth:16.0.1'
|
implementation 'com.google.android.gms:play-services-auth:16.0.1'
|
||||||
implementation 'com.google.android.gms:play-services-vision:16.2.0'
|
implementation 'com.google.android.gms:play-services-vision:16.2.0'
|
||||||
implementation 'com.google.android.gms:play-services-wallet:16.0.1'
|
implementation 'com.google.android.gms:play-services-wallet:16.0.1'
|
||||||
implementation 'com.google.android.gms:play-services-wearable:16.0.1'
|
implementation 'com.google.android.gms:play-services-wearable:16.0.1'
|
||||||
implementation 'com.android.support:support-core-ui:28.0.0'
|
|
||||||
implementation 'com.android.support:support-compat:28.0.0'
|
|
||||||
implementation 'com.android.support:support-core-utils:28.0.0'
|
|
||||||
implementation 'com.android.support:support-v13:28.0.0'
|
|
||||||
implementation 'com.android.support:palette-v7:28.0.0'
|
|
||||||
implementation 'com.android.support:exifinterface:28.0.0'
|
|
||||||
implementation 'net.hockeyapp.android:HockeySDK:5.1.1'
|
implementation 'net.hockeyapp.android:HockeySDK:5.1.1'
|
||||||
implementation 'com.googlecode.mp4parser:isoparser:1.0.6'
|
implementation 'com.googlecode.mp4parser:isoparser:1.0.6'
|
||||||
implementation 'com.stripe:stripe-android:2.0.2'
|
implementation 'com.stripe:stripe-android:2.0.2'
|
||||||
|
@ -89,11 +92,11 @@ android {
|
||||||
debugMultidex {
|
debugMultidex {
|
||||||
initWith debug
|
initWith debug
|
||||||
minifyEnabled false
|
minifyEnabled false
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
dependencies{
|
dependencies {
|
||||||
implementation 'com.android.support:multidex:1.0.3'
|
implementation 'com.android.support:multidex:1.0.3'
|
||||||
}
|
}
|
||||||
manifestPlaceholders = [applicationClassName:"MultiDexApplicationLoader"]
|
manifestPlaceholders = [applicationClassName: "MultiDexApplicationLoader"]
|
||||||
}
|
}
|
||||||
|
|
||||||
HA {
|
HA {
|
||||||
|
@ -115,15 +118,13 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig.versionCode = 1517
|
|
||||||
|
|
||||||
sourceSets.debug {
|
sourceSets.debug {
|
||||||
manifest.srcFile 'config/debug/AndroidManifest.xml'
|
manifest.srcFile 'config/debug/AndroidManifest.xml'
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets.debugMultidex {
|
sourceSets.debugMultidex {
|
||||||
manifest.srcFile 'config/debug/AndroidManifest.xml'
|
manifest.srcFile 'config/debug/AndroidManifest.xml'
|
||||||
java.srcDirs = ['src/multidex/java']
|
java.srcDirs = ['src/multidex/java']
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets.HA {
|
sourceSets.HA {
|
||||||
|
@ -242,12 +243,24 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defaultConfig.versionCode = 1591
|
||||||
|
|
||||||
applicationVariants.all { variant ->
|
applicationVariants.all { variant ->
|
||||||
variant.outputs.all { output ->
|
variant.outputs.all { output ->
|
||||||
output.processManifest.doLast {
|
output.getProcessManifestProvider().get().doLast {
|
||||||
def abiVersion = variant.productFlavors.get(0).abiVersionCode
|
def abiVersion = variant.productFlavors.get(0).abiVersionCode
|
||||||
String manifestPath = "$manifestOutputDirectory/AndroidManifest.xml"
|
|
||||||
|
def outputDir = manifestOutputDirectory
|
||||||
|
File directory
|
||||||
|
if (outputDir instanceof File) {
|
||||||
|
directory = outputDir
|
||||||
|
} else {
|
||||||
|
directory = outputDir.get().asFile
|
||||||
|
}
|
||||||
|
|
||||||
|
String manifestPath = directory.toString() + "/AndroidManifest.xml"
|
||||||
def manifestContent = file(manifestPath).getText()
|
def manifestContent = file(manifestPath).getText()
|
||||||
|
|
||||||
manifestContent = manifestContent.replace(String.format('android:versionCode="%d"', defaultConfig.versionCode), String.format('android:versionCode="%s"', defaultConfig.versionCode * 10 + abiVersion))
|
manifestContent = manifestContent.replace(String.format('android:versionCode="%d"', defaultConfig.versionCode), String.format('android:versionCode="%s"', defaultConfig.versionCode * 10 + abiVersion))
|
||||||
file(manifestPath).write(manifestContent)
|
file(manifestPath).write(manifestContent)
|
||||||
}
|
}
|
||||||
|
@ -256,7 +269,7 @@ android {
|
||||||
|
|
||||||
variantFilter { variant ->
|
variantFilter { variant ->
|
||||||
def names = variant.flavors*.name
|
def names = variant.flavors*.name
|
||||||
if(variant.buildType.name!="release" && !names.contains("afat")){
|
if (variant.buildType.name != "release" && !names.contains("afat")) {
|
||||||
setIgnore(true)
|
setIgnore(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -264,9 +277,9 @@ android {
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 27
|
targetSdkVersion 27
|
||||||
versionName "5.4.0"
|
versionName "5.6.1"
|
||||||
|
|
||||||
vectorDrawables.generatedDensities = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi']
|
vectorDrawables.generatedDensities = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi']
|
||||||
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
ndkBuild {
|
ndkBuild {
|
||||||
|
@ -275,7 +288,7 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
manifestPlaceholders = [applicationClassName:"ApplicationLoader"]
|
manifestPlaceholders = [applicationClassName: "ApplicationLoader"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,11 +19,10 @@
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:icon="@drawable/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:roundIcon="@drawable/ic_launcher"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:label="@string/AppNameBeta"
|
android:label="@string/AppNameBeta"
|
||||||
android:theme="@style/Theme.TMessages.Start"
|
android:theme="@style/Theme.TMessages.Start"
|
||||||
android:name=".ApplicationLoader"
|
|
||||||
android:hardwareAccelerated="@bool/useHardwareAcceleration"
|
android:hardwareAccelerated="@bool/useHardwareAcceleration"
|
||||||
android:largeHeap="true"
|
android:largeHeap="true"
|
||||||
android:supportsRtl="false"
|
android:supportsRtl="false"
|
||||||
|
@ -40,6 +39,8 @@
|
||||||
|
|
||||||
<uses-library android:name="com.google.android.maps" android:required="false"/>
|
<uses-library android:name="com.google.android.maps" android:required="false"/>
|
||||||
|
|
||||||
|
<meta-data android:name="firebase_analytics_collection_deactivated" android:value="true" />
|
||||||
|
<meta-data android:name="google_analytics_adid_collection_enabled" android:value="false" />
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
tools:replace="android:enabled"
|
tools:replace="android:enabled"
|
||||||
|
|
|
@ -15,13 +15,14 @@
|
||||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.CALL_PHONE" />
|
<uses-permission android:name="android.permission.CALL_PHONE" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
|
||||||
<permission android:name="org.telegram.messenger.permission.MAPS_RECEIVE" android:protectionLevel="signature"/>
|
<permission android:name="org.telegram.messenger.permission.MAPS_RECEIVE" android:protectionLevel="signature"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:icon="@drawable/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:roundIcon="@drawable/ic_launcher"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:label="@string/AppNameBeta"
|
android:label="@string/AppNameBeta"
|
||||||
android:theme="@style/Theme.TMessages.Start"
|
android:theme="@style/Theme.TMessages.Start"
|
||||||
android:name=".ApplicationLoader"
|
android:name=".ApplicationLoader"
|
||||||
|
@ -41,6 +42,8 @@
|
||||||
|
|
||||||
<uses-library android:name="com.google.android.maps" android:required="false"/>
|
<uses-library android:name="com.google.android.maps" android:required="false"/>
|
||||||
|
|
||||||
|
<meta-data android:name="firebase_analytics_collection_deactivated" android:value="true" />
|
||||||
|
<meta-data android:name="google_analytics_adid_collection_enabled" android:value="false" />
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
tools:replace="android:enabled"
|
tools:replace="android:enabled"
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
package="org.telegram.messenger"
|
|
||||||
android:installLocation="auto">
|
|
||||||
|
|
||||||
<application
|
|
||||||
android:allowBackup="false"
|
|
||||||
android:icon="@drawable/ic_launcher"
|
|
||||||
android:roundIcon="@drawable/ic_launcher"
|
|
||||||
android:label="@string/AppName"
|
|
||||||
android:theme="@style/Theme.TMessages.Start"
|
|
||||||
android:name=".ApplicationLoader"
|
|
||||||
android:hardwareAccelerated="@bool/useHardwareAcceleration"
|
|
||||||
android:largeHeap="true"
|
|
||||||
android:supportsRtl="false"
|
|
||||||
tools:replace="android:supportsRtl">
|
|
||||||
|
|
||||||
</application>
|
|
||||||
|
|
||||||
</manifest>
|
|
|
@ -19,8 +19,8 @@
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:icon="@drawable/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:roundIcon="@drawable/ic_launcher"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:label="@string/AppName"
|
android:label="@string/AppName"
|
||||||
android:theme="@style/Theme.TMessages.Start"
|
android:theme="@style/Theme.TMessages.Start"
|
||||||
android:name=".ApplicationLoader"
|
android:name=".ApplicationLoader"
|
||||||
|
@ -40,6 +40,9 @@
|
||||||
|
|
||||||
<uses-library android:name="com.google.android.maps" android:required="false"/>
|
<uses-library android:name="com.google.android.maps" android:required="false"/>
|
||||||
|
|
||||||
|
<meta-data android:name="firebase_analytics_collection_deactivated" android:value="true" />
|
||||||
|
<meta-data android:name="google_analytics_adid_collection_enabled" android:value="false" />
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
tools:replace="android:enabled"
|
tools:replace="android:enabled"
|
||||||
android:name="com.google.android.gms.measurement.AppMeasurementReceiver"
|
android:name="com.google.android.gms.measurement.AppMeasurementReceiver"
|
||||||
|
|
|
@ -15,13 +15,14 @@
|
||||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.CALL_PHONE" />
|
<uses-permission android:name="android.permission.CALL_PHONE" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
|
||||||
<permission android:name="org.telegram.messenger.permission.MAPS_RECEIVE" android:protectionLevel="signature"/>
|
<permission android:name="org.telegram.messenger.permission.MAPS_RECEIVE" android:protectionLevel="signature"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:icon="@drawable/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:roundIcon="@drawable/ic_launcher"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:label="@string/AppName"
|
android:label="@string/AppName"
|
||||||
android:theme="@style/Theme.TMessages.Start"
|
android:theme="@style/Theme.TMessages.Start"
|
||||||
android:name=".ApplicationLoader"
|
android:name=".ApplicationLoader"
|
||||||
|
@ -41,6 +42,9 @@
|
||||||
|
|
||||||
<uses-library android:name="com.google.android.maps" android:required="false"/>
|
<uses-library android:name="com.google.android.maps" android:required="false"/>
|
||||||
|
|
||||||
|
<meta-data android:name="firebase_analytics_collection_deactivated" android:value="true" />
|
||||||
|
<meta-data android:name="google_analytics_adid_collection_enabled" android:value="false" />
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
tools:replace="android:enabled"
|
tools:replace="android:enabled"
|
||||||
android:name="com.google.android.gms.measurement.AppMeasurementReceiver"
|
android:name="com.google.android.gms.measurement.AppMeasurementReceiver"
|
||||||
|
|
|
@ -95,6 +95,7 @@ endif
|
||||||
|
|
||||||
include $(PREBUILT_STATIC_LIBRARY)
|
include $(PREBUILT_STATIC_LIBRARY)
|
||||||
|
|
||||||
|
TGVOIP_ADDITIONAL_CFLAGS := -DTGVOIP_NO_VIDEO
|
||||||
include $(MY_LOCAL_PATH)/libtgvoip/Android.mk
|
include $(MY_LOCAL_PATH)/libtgvoip/Android.mk
|
||||||
LOCAL_PATH := $(MY_LOCAL_PATH) # restore local path after include
|
LOCAL_PATH := $(MY_LOCAL_PATH) # restore local path after include
|
||||||
|
|
||||||
|
@ -123,7 +124,6 @@ LOCAL_SRC_FILES := \
|
||||||
./tgnet/Request.cpp \
|
./tgnet/Request.cpp \
|
||||||
./tgnet/Timer.cpp \
|
./tgnet/Timer.cpp \
|
||||||
./tgnet/TLObject.cpp \
|
./tgnet/TLObject.cpp \
|
||||||
./tgnet/FileLoadOperation.cpp \
|
|
||||||
./tgnet/ProxyCheckInfo.cpp \
|
./tgnet/ProxyCheckInfo.cpp \
|
||||||
./tgnet/Handshake.cpp \
|
./tgnet/Handshake.cpp \
|
||||||
./tgnet/Config.cpp
|
./tgnet/Config.cpp
|
||||||
|
@ -547,8 +547,6 @@ LOCAL_SRC_FILES += \
|
||||||
./SqliteWrapper.cpp \
|
./SqliteWrapper.cpp \
|
||||||
./TgNetWrapper.cpp \
|
./TgNetWrapper.cpp \
|
||||||
./NativeLoader.cpp \
|
./NativeLoader.cpp \
|
||||||
./emoji/emoji_suggestions_data.cpp \
|
|
||||||
./emoji/emoji_suggestions.cpp \
|
|
||||||
./exoplayer/flac_jni.cc \
|
./exoplayer/flac_jni.cc \
|
||||||
./exoplayer/flac_parser.cc \
|
./exoplayer/flac_parser.cc \
|
||||||
./exoplayer/opus_jni.cc \
|
./exoplayer/opus_jni.cc \
|
||||||
|
|
|
@ -163,15 +163,20 @@ jlong Java_org_telegram_SQLite_SQLiteDatabase_opendb(JNIEnv *env, jobject object
|
||||||
return (jlong) handle;
|
return (jlong) handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jint Java_org_telegram_SQLite_SQLiteCursor_columnCount(JNIEnv *env, jobject object, jlong statementHandle) {
|
||||||
|
sqlite3_stmt *handle = (sqlite3_stmt *) (intptr_t) statementHandle;
|
||||||
|
return sqlite3_column_count(handle);
|
||||||
|
}
|
||||||
|
|
||||||
jint Java_org_telegram_SQLite_SQLiteCursor_columnType(JNIEnv *env, jobject object, jlong statementHandle, jint columnIndex) {
|
jint Java_org_telegram_SQLite_SQLiteCursor_columnType(JNIEnv *env, jobject object, jlong statementHandle, jint columnIndex) {
|
||||||
sqlite3_stmt *handle = (sqlite3_stmt *) (intptr_t) statementHandle;
|
sqlite3_stmt *handle = (sqlite3_stmt *) (intptr_t) statementHandle;
|
||||||
return sqlite3_column_type(handle, columnIndex);
|
return sqlite3_column_type(handle, columnIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
jboolean Java_org_telegram_SQLite_SQLiteCursor_columnIsNull(JNIEnv *env, jobject object, jlong statementHandle, jint columnIndex) {
|
jint Java_org_telegram_SQLite_SQLiteCursor_columnIsNull(JNIEnv *env, jobject object, jlong statementHandle, jint columnIndex) {
|
||||||
sqlite3_stmt *handle = (sqlite3_stmt *) (intptr_t) statementHandle;
|
sqlite3_stmt *handle = (sqlite3_stmt *) (intptr_t) statementHandle;
|
||||||
int valType = sqlite3_column_type(handle, columnIndex);
|
int valType = sqlite3_column_type(handle, columnIndex);
|
||||||
return (jboolean) (SQLITE_NULL == valType);
|
return SQLITE_NULL == valType ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
jint Java_org_telegram_SQLite_SQLiteCursor_columnIntValue(JNIEnv *env, jobject object, jlong statementHandle, jint columnIndex) {
|
jint Java_org_telegram_SQLite_SQLiteCursor_columnIntValue(JNIEnv *env, jobject object, jlong statementHandle, jint columnIndex) {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include "tgnet/NativeByteBuffer.h"
|
#include "tgnet/NativeByteBuffer.h"
|
||||||
#include "tgnet/ConnectionsManager.h"
|
#include "tgnet/ConnectionsManager.h"
|
||||||
#include "tgnet/MTProtoScheme.h"
|
#include "tgnet/MTProtoScheme.h"
|
||||||
#include "tgnet/FileLoadOperation.h"
|
#include "tgnet/ConnectionSocket.h"
|
||||||
|
|
||||||
JavaVM *java;
|
JavaVM *java;
|
||||||
jclass jclass_RequestDelegateInternal;
|
jclass jclass_RequestDelegateInternal;
|
||||||
|
@ -19,11 +19,6 @@ jmethodID jclass_QuickAckDelegate_run;
|
||||||
jclass jclass_WriteToSocketDelegate;
|
jclass jclass_WriteToSocketDelegate;
|
||||||
jmethodID jclass_WriteToSocketDelegate_run;
|
jmethodID jclass_WriteToSocketDelegate_run;
|
||||||
|
|
||||||
jclass jclass_FileLoadOperationDelegate;
|
|
||||||
jmethodID jclass_FileLoadOperationDelegate_onFinished;
|
|
||||||
jmethodID jclass_FileLoadOperationDelegate_onFailed;
|
|
||||||
jmethodID jclass_FileLoadOperationDelegate_onProgressChanged;
|
|
||||||
|
|
||||||
jclass jclass_ConnectionsManager;
|
jclass jclass_ConnectionsManager;
|
||||||
jmethodID jclass_ConnectionsManager_onUnparsedMessageReceived;
|
jmethodID jclass_ConnectionsManager_onUnparsedMessageReceived;
|
||||||
jmethodID jclass_ConnectionsManager_onUpdate;
|
jmethodID jclass_ConnectionsManager_onUpdate;
|
||||||
|
@ -41,93 +36,6 @@ jmethodID jclass_ConnectionsManager_getInitFlags;
|
||||||
|
|
||||||
bool check_utf8(const char *data, size_t len);
|
bool check_utf8(const char *data, size_t len);
|
||||||
|
|
||||||
/*jint createLoadOpetation(JNIEnv *env, jclass c, jint dc_id, jlong id, jlong volume_id, jlong access_hash, jint local_id, jbyteArray encKey, jbyteArray encIv, jstring extension, jint version, jint size, jstring dest, jstring temp, jobject delegate) {
|
|
||||||
if (encKey != nullptr && encIv == nullptr || encKey == nullptr && encIv != nullptr || extension == nullptr || dest == nullptr || temp == nullptr) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
FileLoadOperation *loadOperation = nullptr;
|
|
||||||
bool error = false;
|
|
||||||
|
|
||||||
const char *extensionStr = env->GetStringUTFChars(extension, NULL);
|
|
||||||
const char *destStr = env->GetStringUTFChars(dest, NULL);
|
|
||||||
const char *tempStr = env->GetStringUTFChars(temp, NULL);
|
|
||||||
|
|
||||||
if (extensionStr == nullptr || destStr == nullptr || tempStr == nullptr) {
|
|
||||||
error = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
jbyte *keyBuff = nullptr;
|
|
||||||
jbyte *ivBuff = nullptr;
|
|
||||||
|
|
||||||
if (!error && encKey != nullptr) {
|
|
||||||
keyBuff = env->GetByteArrayElements(encKey, NULL);
|
|
||||||
ivBuff = env->GetByteArrayElements(encIv, NULL);
|
|
||||||
if (keyBuff == nullptr || ivBuff == nullptr) {
|
|
||||||
error = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!error) {
|
|
||||||
if (delegate != nullptr) {
|
|
||||||
delegate = env->NewGlobalRef(delegate);
|
|
||||||
}
|
|
||||||
loadOperation = new FileLoadOperation(dc_id, id, volume_id, access_hash, local_id, (uint8_t *) keyBuff, (uint8_t *) ivBuff, extensionStr, version, size, destStr, tempStr);
|
|
||||||
loadOperation->setDelegate([delegate](std::string path) {
|
|
||||||
jstring pathText = jniEnv->NewStringUTF(path.c_str());
|
|
||||||
if (delegate != nullptr) {
|
|
||||||
jniEnv->CallVoidMethod(delegate, jclass_FileLoadOperationDelegate_onFinished, pathText);
|
|
||||||
}
|
|
||||||
if (pathText != nullptr) {
|
|
||||||
jniEnv->DeleteLocalRef(pathText);
|
|
||||||
}
|
|
||||||
}, [delegate](FileLoadFailReason reason) {
|
|
||||||
if (delegate != nullptr) {
|
|
||||||
jniEnv->CallVoidMethod(delegate, jclass_FileLoadOperationDelegate_onFailed, reason);
|
|
||||||
}
|
|
||||||
}, [delegate](float progress) {
|
|
||||||
if (delegate != nullptr) {
|
|
||||||
jniEnv->CallVoidMethod(delegate, jclass_FileLoadOperationDelegate_onProgressChanged, progress);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
loadOperation->ptr1 = delegate;
|
|
||||||
}
|
|
||||||
if (keyBuff != nullptr) {
|
|
||||||
env->ReleaseByteArrayElements(encKey, keyBuff, JNI_ABORT);
|
|
||||||
}
|
|
||||||
if (ivBuff != nullptr) {
|
|
||||||
env->ReleaseByteArrayElements(encIv, ivBuff, JNI_ABORT);
|
|
||||||
}
|
|
||||||
if (extensionStr != nullptr) {
|
|
||||||
env->ReleaseStringUTFChars(extension, extensionStr);
|
|
||||||
}
|
|
||||||
if (destStr != nullptr) {
|
|
||||||
env->ReleaseStringUTFChars(dest, destStr);
|
|
||||||
}
|
|
||||||
if (tempStr != nullptr) {
|
|
||||||
env->ReleaseStringUTFChars(temp, tempStr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (jint) loadOperation;
|
|
||||||
}
|
|
||||||
|
|
||||||
void startLoadOperation(JNIEnv *env, jclass c, jint address) {
|
|
||||||
if (address != 0) {
|
|
||||||
((FileLoadOperation *) address)->start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void cancelLoadOperation(JNIEnv *env, jclass c, jint address) {
|
|
||||||
if (address != 0) {
|
|
||||||
((FileLoadOperation *) address)->cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *FileLoadOperationClassPathName = "org/telegram/tgnet/FileLoadOperation";
|
|
||||||
static JNINativeMethod FileLoadOperationMethods[] = {
|
|
||||||
{"native_createLoadOpetation", "(IJJJI[B[BLjava/lang/String;IILjava/lang/String;Ljava/lang/String;Ljava/lang/Object;)I", (void *) createLoadOpetation},
|
|
||||||
{"native_startLoadOperation", "(I)V", (void *) startLoadOperation},
|
|
||||||
{"native_cancelLoadOperation", "(I)V", (void *) cancelLoadOperation}
|
|
||||||
};*/
|
|
||||||
|
|
||||||
jlong getFreeBuffer(JNIEnv *env, jclass c, jint length) {
|
jlong getFreeBuffer(JNIEnv *env, jclass c, jint length) {
|
||||||
return (jlong) (intptr_t) BuffersStorage::getInstance().getFreeBuffer((uint32_t) length);
|
return (jlong) (intptr_t) BuffersStorage::getInstance().getFreeBuffer((uint32_t) length);
|
||||||
}
|
}
|
||||||
|
@ -400,17 +308,10 @@ class Delegate : public ConnectiosManagerDelegate {
|
||||||
jniEnv[instanceNum]->CallStaticVoidMethod(jclass_ConnectionsManager, jclass_ConnectionsManager_onProxyError);
|
jniEnv[instanceNum]->CallStaticVoidMethod(jclass_ConnectionsManager, jclass_ConnectionsManager_onProxyError);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getHostByName(std::string domain, int32_t instanceNum) {
|
void getHostByName(std::string domain, int32_t instanceNum, ConnectionSocket *socket) {
|
||||||
jstring domainName = jniEnv[instanceNum]->NewStringUTF(domain.c_str());
|
jstring domainName = jniEnv[instanceNum]->NewStringUTF(domain.c_str());
|
||||||
jstring address = (jstring) jniEnv[instanceNum]->CallStaticObjectMethod(jclass_ConnectionsManager, jclass_ConnectionsManager_getHostByName, domainName, instanceNum);
|
jniEnv[instanceNum]->CallStaticVoidMethod(jclass_ConnectionsManager, jclass_ConnectionsManager_getHostByName, domainName, (jlong) (intptr_t) socket);
|
||||||
const char *addressStr = jniEnv[instanceNum]->GetStringUTFChars(address, 0);
|
|
||||||
std::string result = std::string(addressStr);
|
|
||||||
if (addressStr != 0) {
|
|
||||||
jniEnv[instanceNum]->ReleaseStringUTFChars(address, addressStr);
|
|
||||||
}
|
|
||||||
jniEnv[instanceNum]->DeleteLocalRef(domainName);
|
jniEnv[instanceNum]->DeleteLocalRef(domainName);
|
||||||
jniEnv[instanceNum]->DeleteLocalRef(address);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t getInitFlags(int32_t instanceNum) {
|
int32_t getInitFlags(int32_t instanceNum) {
|
||||||
|
@ -418,6 +319,21 @@ class Delegate : public ConnectiosManagerDelegate {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void onHostNameResolved(JNIEnv *env, jclass c, jstring host, jlong address, jstring ip) {
|
||||||
|
const char *ipStr = env->GetStringUTFChars(ip, 0);
|
||||||
|
const char *hostStr = env->GetStringUTFChars(host, 0);
|
||||||
|
std::string i = std::string(ipStr);
|
||||||
|
std::string h = std::string(hostStr);
|
||||||
|
if (ipStr != 0) {
|
||||||
|
env->ReleaseStringUTFChars(ip, ipStr);
|
||||||
|
}
|
||||||
|
if (hostStr != 0) {
|
||||||
|
env->ReleaseStringUTFChars(host, hostStr);
|
||||||
|
}
|
||||||
|
ConnectionSocket *socket = (ConnectionSocket *) (intptr_t) address;
|
||||||
|
socket->onHostNameResolved(h, i, false);
|
||||||
|
}
|
||||||
|
|
||||||
void setLangCode(JNIEnv *env, jclass c, jint instanceNum, jstring langCode) {
|
void setLangCode(JNIEnv *env, jclass c, jint instanceNum, jstring langCode) {
|
||||||
const char *langCodeStr = env->GetStringUTFChars(langCode, 0);
|
const char *langCodeStr = env->GetStringUTFChars(langCode, 0);
|
||||||
|
|
||||||
|
@ -506,7 +422,8 @@ static JNINativeMethod ConnectionsManagerMethods[] = {
|
||||||
{"native_setPushConnectionEnabled", "(IZ)V", (void *) setPushConnectionEnabled},
|
{"native_setPushConnectionEnabled", "(IZ)V", (void *) setPushConnectionEnabled},
|
||||||
{"native_setJava", "(Z)V", (void *) setJava},
|
{"native_setJava", "(Z)V", (void *) setJava},
|
||||||
{"native_applyDnsConfig", "(IJLjava/lang/String;)V", (void *) applyDnsConfig},
|
{"native_applyDnsConfig", "(IJLjava/lang/String;)V", (void *) applyDnsConfig},
|
||||||
{"native_checkProxy", "(ILjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/telegram/tgnet/RequestTimeDelegate;)J", (void *) checkProxy}
|
{"native_checkProxy", "(ILjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/telegram/tgnet/RequestTimeDelegate;)J", (void *) checkProxy},
|
||||||
|
{"native_onHostNameResolved", "(Ljava/lang/String;JLjava/lang/String;)V", (void *) onHostNameResolved}
|
||||||
};
|
};
|
||||||
|
|
||||||
inline int registerNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int methodsCount) {
|
inline int registerNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int methodsCount) {
|
||||||
|
@ -527,10 +444,6 @@ extern "C" int registerNativeTgNetFunctions(JavaVM *vm, JNIEnv *env) {
|
||||||
if (!registerNativeMethods(env, NativeByteBufferClassPathName, NativeByteBufferMethods, sizeof(NativeByteBufferMethods) / sizeof(NativeByteBufferMethods[0]))) {
|
if (!registerNativeMethods(env, NativeByteBufferClassPathName, NativeByteBufferMethods, sizeof(NativeByteBufferMethods) / sizeof(NativeByteBufferMethods[0]))) {
|
||||||
return JNI_FALSE;
|
return JNI_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
//if (!registerNativeMethods(env, FileLoadOperationClassPathName, FileLoadOperationMethods, sizeof(FileLoadOperationMethods) / sizeof(FileLoadOperationMethods[0]))) {
|
|
||||||
// return JNI_FALSE;
|
|
||||||
//}
|
|
||||||
|
|
||||||
if (!registerNativeMethods(env, ConnectionsManagerClassPathName, ConnectionsManagerMethods, sizeof(ConnectionsManagerMethods) / sizeof(ConnectionsManagerMethods[0]))) {
|
if (!registerNativeMethods(env, ConnectionsManagerClassPathName, ConnectionsManagerMethods, sizeof(ConnectionsManagerMethods) / sizeof(ConnectionsManagerMethods[0]))) {
|
||||||
return JNI_FALSE;
|
return JNI_FALSE;
|
||||||
|
@ -571,27 +484,6 @@ extern "C" int registerNativeTgNetFunctions(JavaVM *vm, JNIEnv *env) {
|
||||||
if (jclass_WriteToSocketDelegate_run == 0) {
|
if (jclass_WriteToSocketDelegate_run == 0) {
|
||||||
return JNI_FALSE;
|
return JNI_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass_FileLoadOperationDelegate = (jclass) env->NewGlobalRef(env->FindClass("org/telegram/tgnet/FileLoadOperationDelegate"));
|
|
||||||
if (jclass_FileLoadOperationDelegate == 0) {
|
|
||||||
return JNI_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
jclass_FileLoadOperationDelegate_onFinished = env->GetMethodID(jclass_FileLoadOperationDelegate, "onFinished", "(Ljava/lang/String;)V");
|
|
||||||
if (jclass_FileLoadOperationDelegate_onFinished == 0) {
|
|
||||||
return JNI_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
jclass_FileLoadOperationDelegate_onFailed = env->GetMethodID(jclass_FileLoadOperationDelegate, "onFailed", "(I)V");
|
|
||||||
if (jclass_FileLoadOperationDelegate_onFailed == 0) {
|
|
||||||
return JNI_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
jclass_FileLoadOperationDelegate_onProgressChanged = env->GetMethodID(jclass_FileLoadOperationDelegate, "onProgressChanged", "(F)V");
|
|
||||||
if (jclass_FileLoadOperationDelegate_onProgressChanged == 0) {
|
|
||||||
return JNI_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
jclass_ConnectionsManager = (jclass) env->NewGlobalRef(env->FindClass("org/telegram/tgnet/ConnectionsManager"));
|
jclass_ConnectionsManager = (jclass) env->NewGlobalRef(env->FindClass("org/telegram/tgnet/ConnectionsManager"));
|
||||||
if (jclass_ConnectionsManager == 0) {
|
if (jclass_ConnectionsManager == 0) {
|
||||||
return JNI_FALSE;
|
return JNI_FALSE;
|
||||||
|
@ -640,7 +532,7 @@ extern "C" int registerNativeTgNetFunctions(JavaVM *vm, JNIEnv *env) {
|
||||||
if (jclass_ConnectionsManager_onProxyError == 0) {
|
if (jclass_ConnectionsManager_onProxyError == 0) {
|
||||||
return JNI_FALSE;
|
return JNI_FALSE;
|
||||||
}
|
}
|
||||||
jclass_ConnectionsManager_getHostByName = env->GetStaticMethodID(jclass_ConnectionsManager, "getHostByName", "(Ljava/lang/String;I)Ljava/lang/String;");
|
jclass_ConnectionsManager_getHostByName = env->GetStaticMethodID(jclass_ConnectionsManager, "getHostByName", "(Ljava/lang/String;J)V");
|
||||||
if (jclass_ConnectionsManager_getHostByName == 0) {
|
if (jclass_ConnectionsManager_getHostByName == 0) {
|
||||||
return JNI_FALSE;
|
return JNI_FALSE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,452 +0,0 @@
|
||||||
/*
|
|
||||||
This file is part of Telegram Desktop,
|
|
||||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
|
||||||
|
|
||||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
It is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
In addition, as a special exception, the copyright holders give permission
|
|
||||||
to link the code of portions of this program with the OpenSSL library.
|
|
||||||
|
|
||||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
|
||||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "emoji_suggestions.h"
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
#include <algorithm>
|
|
||||||
#include "emoji_suggestions_data.h"
|
|
||||||
|
|
||||||
checksum Crc32Table[256];
|
|
||||||
class Crc32Initializer {
|
|
||||||
public:
|
|
||||||
Crc32Initializer() {
|
|
||||||
checksum poly = 0x04C11DB7U;
|
|
||||||
for (auto i = 0; i != 256; ++i) {
|
|
||||||
Crc32Table[i] = reflect(i, 8) << 24;
|
|
||||||
for (auto j = 0; j != 8; ++j) {
|
|
||||||
Crc32Table[i] = (Crc32Table[i] << 1) ^ (Crc32Table[i] & (1 << 31) ? poly : 0);
|
|
||||||
}
|
|
||||||
Crc32Table[i] = reflect(Crc32Table[i], 32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
checksum reflect(checksum val, char ch) {
|
|
||||||
checksum result = 0;
|
|
||||||
for (int i = 1; i < (ch + 1); ++i) {
|
|
||||||
if (val & 1) {
|
|
||||||
result |= 1 << (ch - i);
|
|
||||||
}
|
|
||||||
val >>= 1;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
checksum countChecksum(const void *data, std::size_t size) {
|
|
||||||
static Crc32Initializer InitTable;
|
|
||||||
|
|
||||||
auto buffer = static_cast<const unsigned char*>(data);
|
|
||||||
auto result = checksum(0xFFFFFFFFU);
|
|
||||||
for (auto i = std::size_t(0); i != size; ++i) {
|
|
||||||
result = (result >> 8) ^ Crc32Table[(result & 0xFFU) ^ buffer[i]];
|
|
||||||
}
|
|
||||||
return (result ^ 0xFFFFFFFFU);
|
|
||||||
}
|
|
||||||
|
|
||||||
class string_span {
|
|
||||||
public:
|
|
||||||
string_span() = default;
|
|
||||||
string_span(const utf16string *data, std::size_t size) : begin_(data), size_(size) {
|
|
||||||
}
|
|
||||||
string_span(const std::vector<utf16string> &data) : begin_(data.data()), size_(data.size()) {
|
|
||||||
}
|
|
||||||
string_span(const string_span &other) = default;
|
|
||||||
string_span &operator=(const string_span &other) = default;
|
|
||||||
|
|
||||||
const utf16string *begin() const {
|
|
||||||
return begin_;
|
|
||||||
}
|
|
||||||
const utf16string *end() const {
|
|
||||||
return begin_ + size_;
|
|
||||||
}
|
|
||||||
std::size_t size() const {
|
|
||||||
return size_;
|
|
||||||
}
|
|
||||||
|
|
||||||
string_span subspan(std::size_t offset, std::size_t size) {
|
|
||||||
return string_span(begin_ + offset, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
const utf16string *begin_ = nullptr;
|
|
||||||
std::size_t size_ = 0;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
bool IsNumber(utf16char ch) {
|
|
||||||
return (ch >= '0' && ch <= '9');
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsLetterOrNumber(utf16char ch) {
|
|
||||||
return (ch >= 'a' && ch <= 'z') || IsNumber(ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
using Replacement = Replacement;
|
|
||||||
|
|
||||||
class Completer {
|
|
||||||
public:
|
|
||||||
Completer(utf16string query);
|
|
||||||
|
|
||||||
std::vector<Suggestion> resolve();
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct Result {
|
|
||||||
const Replacement *replacement;
|
|
||||||
int wordsUsed;
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::vector<utf16char> NormalizeQuery(utf16string query);
|
|
||||||
void addResult(const Replacement *replacement);
|
|
||||||
bool isDuplicateOfLastResult(const Replacement *replacement) const;
|
|
||||||
bool isBetterThanLastResult(const Replacement *replacement) const;
|
|
||||||
void processInitialList();
|
|
||||||
void filterInitialList();
|
|
||||||
void initWordsTracking();
|
|
||||||
bool matchQueryForCurrentItem();
|
|
||||||
bool matchQueryTailStartingFrom(int position);
|
|
||||||
string_span findWordsStartingWith(utf16char ch);
|
|
||||||
int findEqualCharsCount(int position, const utf16string *word);
|
|
||||||
std::vector<Suggestion> prepareResult();
|
|
||||||
bool startsWithQuery(utf16string word);
|
|
||||||
bool isExactMatch(utf16string replacement);
|
|
||||||
|
|
||||||
std::vector<Result> _result;
|
|
||||||
|
|
||||||
utf16string _initialQuery;
|
|
||||||
const std::vector<utf16char> _query;
|
|
||||||
const utf16char *_queryBegin = nullptr;
|
|
||||||
int _querySize = 0;
|
|
||||||
|
|
||||||
const std::vector<const Replacement*> *_initialList = nullptr;
|
|
||||||
|
|
||||||
string_span _currentItemWords;
|
|
||||||
int _currentItemWordsUsedCount = 0;
|
|
||||||
|
|
||||||
class UsedWordGuard {
|
|
||||||
public:
|
|
||||||
UsedWordGuard(std::vector<small> &map, int index);
|
|
||||||
UsedWordGuard(const UsedWordGuard &other) = delete;
|
|
||||||
UsedWordGuard(UsedWordGuard &&other);
|
|
||||||
UsedWordGuard &operator=(const UsedWordGuard &other) = delete;
|
|
||||||
UsedWordGuard &operator=(UsedWordGuard &&other) = delete;
|
|
||||||
explicit operator bool() const;
|
|
||||||
~UsedWordGuard();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<small> &_map;
|
|
||||||
int _index = 0;
|
|
||||||
bool _guarded = false;
|
|
||||||
|
|
||||||
};
|
|
||||||
std::vector<small> _currentItemWordsUsedMap;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
Completer::UsedWordGuard::UsedWordGuard(std::vector<small> &map, int index) : _map(map), _index(index) {
|
|
||||||
if (!_map[_index]) {
|
|
||||||
_guarded = _map[_index] = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Completer::UsedWordGuard::UsedWordGuard(UsedWordGuard &&other) : _map(other._map), _index(other._index), _guarded(other._guarded) {
|
|
||||||
other._guarded = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Completer::UsedWordGuard::operator bool() const {
|
|
||||||
return _guarded;
|
|
||||||
}
|
|
||||||
|
|
||||||
Completer::UsedWordGuard::~UsedWordGuard() {
|
|
||||||
if (_guarded) {
|
|
||||||
_map[_index] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Completer::Completer(utf16string query) : _initialQuery(query), _query(NormalizeQuery(query)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove all non-letters-or-numbers.
|
|
||||||
// Leave '-' and '+' only if they're followed by a number or
|
|
||||||
// at the end of the query (so it is possibly followed by a number).
|
|
||||||
std::vector<utf16char> Completer::NormalizeQuery(utf16string query) {
|
|
||||||
auto result = std::vector<utf16char>();
|
|
||||||
result.reserve(query.size());
|
|
||||||
auto copyFrom = query.data();
|
|
||||||
auto e = copyFrom + query.size();
|
|
||||||
auto copyTo = result.data();
|
|
||||||
for (auto i = query.data(); i != e; ++i) {
|
|
||||||
if (IsLetterOrNumber(*i)) {
|
|
||||||
continue;
|
|
||||||
} else if (*i == '-' || *i == '+') {
|
|
||||||
if (i + 1 == e || IsNumber(*(i + 1))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i > copyFrom) {
|
|
||||||
result.resize(result.size() + (i - copyFrom));
|
|
||||||
memcpy(copyTo, copyFrom, (i - copyFrom) * sizeof(utf16char));
|
|
||||||
copyTo += (i - copyFrom);
|
|
||||||
}
|
|
||||||
copyFrom = i + 1;
|
|
||||||
}
|
|
||||||
if (e > copyFrom) {
|
|
||||||
result.resize(result.size() + (e - copyFrom));
|
|
||||||
memcpy(copyTo, copyFrom, (e - copyFrom) * sizeof(utf16char));
|
|
||||||
copyTo += (e - copyFrom);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Suggestion> Completer::resolve() {
|
|
||||||
_queryBegin = _query.data();
|
|
||||||
_querySize = _query.size();
|
|
||||||
if (!_querySize) {
|
|
||||||
return std::vector<Suggestion>();
|
|
||||||
}
|
|
||||||
_initialList = GetReplacements(*_queryBegin);
|
|
||||||
if (!_initialList) {
|
|
||||||
return std::vector<Suggestion>();
|
|
||||||
}
|
|
||||||
_result.reserve(_initialList->size());
|
|
||||||
processInitialList();
|
|
||||||
return prepareResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Completer::isDuplicateOfLastResult(const Replacement *item) const {
|
|
||||||
if (_result.empty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return (_result.back().replacement->emoji == item->emoji);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Completer::isBetterThanLastResult(const Replacement *item) const {
|
|
||||||
auto &last = _result.back();
|
|
||||||
if (_currentItemWordsUsedCount < last.wordsUsed) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto firstCharOfQuery = _query[0];
|
|
||||||
auto firstCharAfterColonLast = last.replacement->replacement[1];
|
|
||||||
auto firstCharAfterColonCurrent = item->replacement[1];
|
|
||||||
auto goodLast = (firstCharAfterColonLast == firstCharOfQuery);
|
|
||||||
auto goodCurrent = (firstCharAfterColonCurrent == firstCharOfQuery);
|
|
||||||
return !goodLast && goodCurrent;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Completer::addResult(const Replacement *item) {
|
|
||||||
if (!isDuplicateOfLastResult(item)) {
|
|
||||||
_result.push_back({ item, _currentItemWordsUsedCount });
|
|
||||||
} else if (isBetterThanLastResult(item)) {
|
|
||||||
_result.back() = { item, _currentItemWordsUsedCount };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Completer::processInitialList() {
|
|
||||||
if (_querySize > 1) {
|
|
||||||
filterInitialList();
|
|
||||||
} else {
|
|
||||||
_currentItemWordsUsedCount = 1;
|
|
||||||
for (auto item : *_initialList) {
|
|
||||||
addResult(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Completer::initWordsTracking() {
|
|
||||||
auto maxWordsCount = 0;
|
|
||||||
for (auto item : *_initialList) {
|
|
||||||
auto wordsCount = item->words.size();
|
|
||||||
if (maxWordsCount < wordsCount) {
|
|
||||||
maxWordsCount = wordsCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_currentItemWordsUsedMap = std::vector<small>(maxWordsCount, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Completer::filterInitialList() {
|
|
||||||
initWordsTracking();
|
|
||||||
for (auto item : *_initialList) {
|
|
||||||
_currentItemWords = string_span(item->words);
|
|
||||||
_currentItemWordsUsedCount = 1;
|
|
||||||
if (matchQueryForCurrentItem()) {
|
|
||||||
addResult(item);
|
|
||||||
}
|
|
||||||
_currentItemWordsUsedCount = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Completer::matchQueryForCurrentItem() {
|
|
||||||
if (_currentItemWords.size() < 2) {
|
|
||||||
return startsWithQuery(*_currentItemWords.begin());
|
|
||||||
}
|
|
||||||
return matchQueryTailStartingFrom(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Completer::startsWithQuery(utf16string word) {
|
|
||||||
if (word.size() < _query.size()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (auto i = std::size_t(0), size = _query.size(); i != size; ++i) {
|
|
||||||
if (word[i] != _query[i]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Completer::isExactMatch(utf16string replacement) {
|
|
||||||
if (replacement.size() != _initialQuery.size() + 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (auto i = std::size_t(0), size = _initialQuery.size(); i != size; ++i) {
|
|
||||||
if (replacement[i] != _initialQuery[i]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Completer::matchQueryTailStartingFrom(int position) {
|
|
||||||
auto charsLeftToMatch = (_querySize - position);
|
|
||||||
if (!charsLeftToMatch) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto firstCharToMatch = *(_queryBegin + position);
|
|
||||||
auto foundWords = findWordsStartingWith(firstCharToMatch);
|
|
||||||
|
|
||||||
for (auto word = foundWords.begin(), foundWordsEnd = word + foundWords.size(); word != foundWordsEnd; ++word) {
|
|
||||||
auto wordIndex = word - _currentItemWords.begin();
|
|
||||||
if (auto guard = UsedWordGuard(_currentItemWordsUsedMap, wordIndex)) {
|
|
||||||
++_currentItemWordsUsedCount;
|
|
||||||
auto equalCharsCount = findEqualCharsCount(position, word);
|
|
||||||
for (auto check = equalCharsCount; check != 0; --check) {
|
|
||||||
if (matchQueryTailStartingFrom(position + check)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
--_currentItemWordsUsedCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Completer::findEqualCharsCount(int position, const utf16string *word) {
|
|
||||||
auto charsLeft = (_querySize - position);
|
|
||||||
auto wordBegin = word->data();
|
|
||||||
auto wordSize = word->size();
|
|
||||||
auto possibleEqualCharsCount = (charsLeft > wordSize ? wordSize : charsLeft);
|
|
||||||
for (auto equalTill = 1; equalTill != possibleEqualCharsCount; ++equalTill) {
|
|
||||||
auto wordCh = *(wordBegin + equalTill);
|
|
||||||
auto queryCh = *(_queryBegin + position + equalTill);
|
|
||||||
if (wordCh != queryCh) {
|
|
||||||
return equalTill;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return possibleEqualCharsCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Suggestion> Completer::prepareResult() {
|
|
||||||
auto firstCharOfQuery = _query[0];
|
|
||||||
std::stable_partition(_result.begin(), _result.end(), [firstCharOfQuery](Result &result) {
|
|
||||||
auto firstCharAfterColon = result.replacement->replacement[1];
|
|
||||||
return (firstCharAfterColon == firstCharOfQuery);
|
|
||||||
});
|
|
||||||
std::stable_partition(_result.begin(), _result.end(), [](Result &result) {
|
|
||||||
return (result.wordsUsed < 2);
|
|
||||||
});
|
|
||||||
std::stable_partition(_result.begin(), _result.end(), [](Result &result) {
|
|
||||||
return (result.wordsUsed < 3);
|
|
||||||
});
|
|
||||||
std::stable_partition(_result.begin(), _result.end(), [this](Result &result) {
|
|
||||||
return isExactMatch(result.replacement->replacement);
|
|
||||||
});
|
|
||||||
|
|
||||||
auto result = std::vector<Suggestion>();
|
|
||||||
result.reserve(_result.size());
|
|
||||||
for (auto &item : _result) {
|
|
||||||
result.emplace_back(item.replacement->emoji, item.replacement->replacement, item.replacement->replacement);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
string_span Completer::findWordsStartingWith(utf16char ch) {
|
|
||||||
auto begin = std::lower_bound(_currentItemWords.begin(), _currentItemWords.end(), ch, [](utf16string word, utf16char ch) {
|
|
||||||
return word[0] < ch;
|
|
||||||
});
|
|
||||||
auto end = std::upper_bound(_currentItemWords.begin(), _currentItemWords.end(), ch, [](utf16char ch, utf16string word) {
|
|
||||||
return ch < word[0];
|
|
||||||
});
|
|
||||||
return _currentItemWords.subspan(begin - _currentItemWords.begin(), end - begin);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Suggestion> GetSuggestions(utf16string query) {
|
|
||||||
return Completer(query).resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetSuggestionMaxLength() {
|
|
||||||
return kReplacementMaxLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
jclass jclass_Suggestion = nullptr;
|
|
||||||
jmethodID jclass_Suggestion_constructor;
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
jobjectArray
|
|
||||||
Java_org_telegram_messenger_Emoji_getSuggestion(JNIEnv *env, jobject object, jstring query) {
|
|
||||||
const jchar *raw = env->GetStringChars(query, 0);
|
|
||||||
jsize len = env->GetStringLength(query);
|
|
||||||
std::vector<Suggestion> suggestions = GetSuggestions(utf16string(raw, len));
|
|
||||||
env->ReleaseStringChars(query, raw);
|
|
||||||
|
|
||||||
if (suggestions.empty()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jclass_Suggestion == nullptr) {
|
|
||||||
jclass_Suggestion = (jclass) env->NewGlobalRef(env->FindClass("org/telegram/messenger/EmojiSuggestion"));
|
|
||||||
jclass_Suggestion_constructor = env->GetMethodID(jclass_Suggestion, "<init>",
|
|
||||||
"(Ljava/lang/String;Ljava/lang/String;)V");
|
|
||||||
}
|
|
||||||
|
|
||||||
jobjectArray result = env->NewObjectArray(suggestions.size(), jclass_Suggestion, nullptr);
|
|
||||||
size_t size = suggestions.size();
|
|
||||||
for (size_t a = 0; a < size; a++) {
|
|
||||||
Suggestion &suggestion = suggestions[a];
|
|
||||||
utf16string emoji = suggestion.emoji();
|
|
||||||
utf16string label = suggestion.label();
|
|
||||||
jstring emojiStr = env->NewString(emoji.data(), emoji.size());
|
|
||||||
jstring labelStr = env->NewString(label.data(), label.size());
|
|
||||||
object = env->NewObject(jclass_Suggestion, jclass_Suggestion_constructor, emojiStr, labelStr);
|
|
||||||
env->SetObjectArrayElement(result, a, object);
|
|
||||||
env->DeleteLocalRef(object);
|
|
||||||
env->DeleteLocalRef(emojiStr);
|
|
||||||
env->DeleteLocalRef(labelStr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,97 +0,0 @@
|
||||||
/*
|
|
||||||
This file is part of Telegram Desktop,
|
|
||||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
|
||||||
|
|
||||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
It is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
In addition, as a special exception, the copyright holders give permission
|
|
||||||
to link the code of portions of this program with the OpenSSL library.
|
|
||||||
|
|
||||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
|
||||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
using small = unsigned char;
|
|
||||||
using medium = unsigned short;
|
|
||||||
using utf16char = unsigned short;
|
|
||||||
|
|
||||||
static_assert(sizeof(utf16char) == 2, "Bad UTF-16 character size.");
|
|
||||||
|
|
||||||
class utf16string {
|
|
||||||
public:
|
|
||||||
utf16string() = default;
|
|
||||||
utf16string(const utf16char *data, std::size_t size) : data_(data), size_(size) {
|
|
||||||
}
|
|
||||||
utf16string(const utf16string &other) = default;
|
|
||||||
utf16string &operator=(const utf16string &other) = default;
|
|
||||||
|
|
||||||
const utf16char *data() const {
|
|
||||||
return data_;
|
|
||||||
}
|
|
||||||
std::size_t size() const {
|
|
||||||
return size_;
|
|
||||||
}
|
|
||||||
|
|
||||||
utf16char operator[](int index) const {
|
|
||||||
return data_[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
const utf16char *data_ = nullptr;
|
|
||||||
std::size_t size_ = 0;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
inline bool operator==(utf16string a, utf16string b) {
|
|
||||||
return (a.size() == b.size()) && (!a.size() || !memcmp(a.data(), b.data(), a.size() * sizeof(utf16char)));
|
|
||||||
}
|
|
||||||
|
|
||||||
using checksum = unsigned int;
|
|
||||||
checksum countChecksum(const void *data, std::size_t size);
|
|
||||||
|
|
||||||
utf16string GetReplacementEmoji(utf16string replacement);
|
|
||||||
|
|
||||||
class Suggestion {
|
|
||||||
public:
|
|
||||||
Suggestion() = default;
|
|
||||||
Suggestion(utf16string emoji, utf16string label, utf16string replacement) : emoji_(emoji), label_(label), replacement_(replacement) {
|
|
||||||
}
|
|
||||||
Suggestion(const Suggestion &other) = default;
|
|
||||||
Suggestion &operator=(const Suggestion &other) = default;
|
|
||||||
|
|
||||||
utf16string emoji() const {
|
|
||||||
return emoji_;
|
|
||||||
}
|
|
||||||
utf16string label() const {
|
|
||||||
return label_;
|
|
||||||
}
|
|
||||||
utf16string replacement() const {
|
|
||||||
return replacement_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
utf16string emoji_;
|
|
||||||
utf16string label_;
|
|
||||||
utf16string replacement_;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<Suggestion> GetSuggestions(utf16string query);
|
|
||||||
|
|
||||||
inline utf16string GetSuggestionEmoji(utf16string replacement) {
|
|
||||||
return GetReplacementEmoji(replacement);
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetSuggestionMaxLength();
|
|
|
@ -1,38 +0,0 @@
|
||||||
/*
|
|
||||||
WARNING! All changes made in this file will be lost!
|
|
||||||
Created from 'empty' by 'codegen_emoji'
|
|
||||||
|
|
||||||
This file is part of Telegram Desktop,
|
|
||||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
|
||||||
|
|
||||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
It is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
In addition, as a special exception, the copyright holders give permission
|
|
||||||
to link the code of portions of this program with the OpenSSL library.
|
|
||||||
|
|
||||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
|
||||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "emoji_suggestions.h"
|
|
||||||
|
|
||||||
struct Replacement {
|
|
||||||
utf16string emoji;
|
|
||||||
utf16string replacement;
|
|
||||||
std::vector<utf16string> words;
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr auto kReplacementMaxLength = 55;
|
|
||||||
|
|
||||||
void InitReplacements();
|
|
||||||
const std::vector<const Replacement*> *GetReplacements(utf16char first);
|
|
||||||
utf16string GetReplacementEmoji(utf16string replacement);
|
|
41
TMessagesProj/jni/ffmpeg/include/libavformat/dv.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* General DV muxer/demuxer
|
||||||
|
* Copyright (c) 2003 Roman Shaposhnik
|
||||||
|
*
|
||||||
|
* Many thanks to Dan Dennedy <dan@dennedy.org> for providing wealth
|
||||||
|
* of DV technical info.
|
||||||
|
*
|
||||||
|
* Raw DV format
|
||||||
|
* Copyright (c) 2002 Fabrice Bellard
|
||||||
|
*
|
||||||
|
* This file is part of FFmpeg.
|
||||||
|
*
|
||||||
|
* FFmpeg is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* FFmpeg is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with FFmpeg; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef AVFORMAT_DV_H
|
||||||
|
#define AVFORMAT_DV_H
|
||||||
|
|
||||||
|
#include "avformat.h"
|
||||||
|
|
||||||
|
typedef struct DVDemuxContext DVDemuxContext;
|
||||||
|
DVDemuxContext* avpriv_dv_init_demux(AVFormatContext* s);
|
||||||
|
int avpriv_dv_get_packet(DVDemuxContext*, AVPacket *);
|
||||||
|
int avpriv_dv_produce_packet(DVDemuxContext*, AVPacket*, uint8_t*, int, int64_t);
|
||||||
|
void ff_dv_offset_reset(DVDemuxContext *c, int64_t frame_offset);
|
||||||
|
|
||||||
|
typedef struct DVMuxContext DVMuxContext;
|
||||||
|
|
||||||
|
#endif /* AVFORMAT_DV_H */
|
367
TMessagesProj/jni/ffmpeg/include/libavformat/isom.h
Normal file
|
@ -0,0 +1,367 @@
|
||||||
|
/*
|
||||||
|
* ISO Media common code
|
||||||
|
* copyright (c) 2001 Fabrice Bellard
|
||||||
|
* copyright (c) 2002 Francois Revol <revol@free.fr>
|
||||||
|
* copyright (c) 2006 Baptiste Coudurier <baptiste.coudurier@free.fr>
|
||||||
|
*
|
||||||
|
* This file is part of FFmpeg.
|
||||||
|
*
|
||||||
|
* FFmpeg is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* FFmpeg is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with FFmpeg; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef AVFORMAT_ISOM_H
|
||||||
|
#define AVFORMAT_ISOM_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "libavutil/encryption_info.h"
|
||||||
|
#include "libavutil/mastering_display_metadata.h"
|
||||||
|
#include "libavutil/spherical.h"
|
||||||
|
#include "libavutil/stereo3d.h"
|
||||||
|
|
||||||
|
#include "avio.h"
|
||||||
|
#include "internal.h"
|
||||||
|
#include "dv.h"
|
||||||
|
|
||||||
|
/* isom.c */
|
||||||
|
extern const AVCodecTag ff_mp4_obj_type[];
|
||||||
|
extern const AVCodecTag ff_codec_movvideo_tags[];
|
||||||
|
extern const AVCodecTag ff_codec_movaudio_tags[];
|
||||||
|
extern const AVCodecTag ff_codec_movsubtitle_tags[];
|
||||||
|
extern const AVCodecTag ff_codec_movdata_tags[];
|
||||||
|
|
||||||
|
int ff_mov_iso639_to_lang(const char lang[4], int mp4);
|
||||||
|
int ff_mov_lang_to_iso639(unsigned code, char to[4]);
|
||||||
|
|
||||||
|
struct AVAESCTR;
|
||||||
|
|
||||||
|
/* the QuickTime file format is quite convoluted...
|
||||||
|
* it has lots of index tables, each indexing something in another one...
|
||||||
|
* Here we just use what is needed to read the chunks
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct MOVStts {
|
||||||
|
unsigned int count;
|
||||||
|
int duration;
|
||||||
|
} MOVStts;
|
||||||
|
|
||||||
|
typedef struct MOVStsc {
|
||||||
|
int first;
|
||||||
|
int count;
|
||||||
|
int id;
|
||||||
|
} MOVStsc;
|
||||||
|
|
||||||
|
typedef struct MOVElst {
|
||||||
|
int64_t duration;
|
||||||
|
int64_t time;
|
||||||
|
float rate;
|
||||||
|
} MOVElst;
|
||||||
|
|
||||||
|
typedef struct MOVDref {
|
||||||
|
uint32_t type;
|
||||||
|
char *path;
|
||||||
|
char *dir;
|
||||||
|
char volume[28];
|
||||||
|
char filename[64];
|
||||||
|
int16_t nlvl_to, nlvl_from;
|
||||||
|
} MOVDref;
|
||||||
|
|
||||||
|
typedef struct MOVAtom {
|
||||||
|
uint32_t type;
|
||||||
|
int64_t size; /* total size (excluding the size and type fields) */
|
||||||
|
} MOVAtom;
|
||||||
|
|
||||||
|
struct MOVParseTableEntry;
|
||||||
|
|
||||||
|
typedef struct MOVFragment {
|
||||||
|
int found_tfhd;
|
||||||
|
unsigned track_id;
|
||||||
|
uint64_t base_data_offset;
|
||||||
|
uint64_t moof_offset;
|
||||||
|
uint64_t implicit_offset;
|
||||||
|
unsigned stsd_id;
|
||||||
|
unsigned duration;
|
||||||
|
unsigned size;
|
||||||
|
unsigned flags;
|
||||||
|
} MOVFragment;
|
||||||
|
|
||||||
|
typedef struct MOVTrackExt {
|
||||||
|
unsigned track_id;
|
||||||
|
unsigned stsd_id;
|
||||||
|
unsigned duration;
|
||||||
|
unsigned size;
|
||||||
|
unsigned flags;
|
||||||
|
} MOVTrackExt;
|
||||||
|
|
||||||
|
typedef struct MOVSbgp {
|
||||||
|
unsigned int count;
|
||||||
|
unsigned int index;
|
||||||
|
} MOVSbgp;
|
||||||
|
|
||||||
|
typedef struct MOVEncryptionIndex {
|
||||||
|
// Individual encrypted samples. If there are no elements, then the default
|
||||||
|
// settings will be used.
|
||||||
|
unsigned int nb_encrypted_samples;
|
||||||
|
AVEncryptionInfo **encrypted_samples;
|
||||||
|
|
||||||
|
uint8_t* auxiliary_info_sizes;
|
||||||
|
size_t auxiliary_info_sample_count;
|
||||||
|
uint8_t auxiliary_info_default_size;
|
||||||
|
uint64_t* auxiliary_offsets; ///< Absolute seek position
|
||||||
|
size_t auxiliary_offsets_count;
|
||||||
|
} MOVEncryptionIndex;
|
||||||
|
|
||||||
|
typedef struct MOVFragmentStreamInfo {
|
||||||
|
int id;
|
||||||
|
int64_t sidx_pts;
|
||||||
|
int64_t first_tfra_pts;
|
||||||
|
int64_t tfdt_dts;
|
||||||
|
int index_entry;
|
||||||
|
MOVEncryptionIndex *encryption_index;
|
||||||
|
} MOVFragmentStreamInfo;
|
||||||
|
|
||||||
|
typedef struct MOVFragmentIndexItem {
|
||||||
|
int64_t moof_offset;
|
||||||
|
int headers_read;
|
||||||
|
int current;
|
||||||
|
int nb_stream_info;
|
||||||
|
MOVFragmentStreamInfo * stream_info;
|
||||||
|
} MOVFragmentIndexItem;
|
||||||
|
|
||||||
|
typedef struct MOVFragmentIndex {
|
||||||
|
int allocated_size;
|
||||||
|
int complete;
|
||||||
|
int current;
|
||||||
|
int nb_items;
|
||||||
|
MOVFragmentIndexItem * item;
|
||||||
|
} MOVFragmentIndex;
|
||||||
|
|
||||||
|
typedef struct MOVIndexRange {
|
||||||
|
int64_t start;
|
||||||
|
int64_t end;
|
||||||
|
} MOVIndexRange;
|
||||||
|
|
||||||
|
typedef struct MOVStreamContext {
|
||||||
|
AVIOContext *pb;
|
||||||
|
int pb_is_copied;
|
||||||
|
int ffindex; ///< AVStream index
|
||||||
|
int next_chunk;
|
||||||
|
unsigned int chunk_count;
|
||||||
|
int64_t *chunk_offsets;
|
||||||
|
unsigned int stts_count;
|
||||||
|
MOVStts *stts_data;
|
||||||
|
unsigned int ctts_count;
|
||||||
|
unsigned int ctts_allocated_size;
|
||||||
|
MOVStts *ctts_data;
|
||||||
|
unsigned int stsc_count;
|
||||||
|
MOVStsc *stsc_data;
|
||||||
|
unsigned int stsc_index;
|
||||||
|
int stsc_sample;
|
||||||
|
unsigned int stps_count;
|
||||||
|
unsigned *stps_data; ///< partial sync sample for mpeg-2 open gop
|
||||||
|
MOVElst *elst_data;
|
||||||
|
unsigned int elst_count;
|
||||||
|
int ctts_index;
|
||||||
|
int ctts_sample;
|
||||||
|
unsigned int sample_size; ///< may contain value calculated from stsd or value from stsz atom
|
||||||
|
unsigned int stsz_sample_size; ///< always contains sample size from stsz atom
|
||||||
|
unsigned int sample_count;
|
||||||
|
int *sample_sizes;
|
||||||
|
int keyframe_absent;
|
||||||
|
unsigned int keyframe_count;
|
||||||
|
int *keyframes;
|
||||||
|
int time_scale;
|
||||||
|
int64_t time_offset; ///< time offset of the edit list entries
|
||||||
|
int64_t min_corrected_pts; ///< minimum Composition time shown by the edits excluding empty edits.
|
||||||
|
int current_sample;
|
||||||
|
int64_t current_index;
|
||||||
|
MOVIndexRange* index_ranges;
|
||||||
|
MOVIndexRange* current_index_range;
|
||||||
|
unsigned int bytes_per_frame;
|
||||||
|
unsigned int samples_per_frame;
|
||||||
|
int dv_audio_container;
|
||||||
|
int pseudo_stream_id; ///< -1 means demux all ids
|
||||||
|
int16_t audio_cid; ///< stsd audio compression id
|
||||||
|
unsigned drefs_count;
|
||||||
|
MOVDref *drefs;
|
||||||
|
int dref_id;
|
||||||
|
int timecode_track;
|
||||||
|
int width; ///< tkhd width
|
||||||
|
int height; ///< tkhd height
|
||||||
|
int dts_shift; ///< dts shift when ctts is negative
|
||||||
|
uint32_t palette[256];
|
||||||
|
int has_palette;
|
||||||
|
int64_t data_size;
|
||||||
|
uint32_t tmcd_flags; ///< tmcd track flags
|
||||||
|
int64_t track_end; ///< used for dts generation in fragmented movie files
|
||||||
|
int start_pad; ///< amount of samples to skip due to enc-dec delay
|
||||||
|
unsigned int rap_group_count;
|
||||||
|
MOVSbgp *rap_group;
|
||||||
|
|
||||||
|
int nb_frames_for_fps;
|
||||||
|
int64_t duration_for_fps;
|
||||||
|
|
||||||
|
/** extradata array (and size) for multiple stsd */
|
||||||
|
uint8_t **extradata;
|
||||||
|
int *extradata_size;
|
||||||
|
int last_stsd_index;
|
||||||
|
int stsd_count;
|
||||||
|
int stsd_version;
|
||||||
|
|
||||||
|
int32_t *display_matrix;
|
||||||
|
AVStereo3D *stereo3d;
|
||||||
|
AVSphericalMapping *spherical;
|
||||||
|
size_t spherical_size;
|
||||||
|
AVMasteringDisplayMetadata *mastering;
|
||||||
|
AVContentLightMetadata *coll;
|
||||||
|
size_t coll_size;
|
||||||
|
|
||||||
|
uint32_t format;
|
||||||
|
|
||||||
|
int has_sidx; // If there is an sidx entry for this stream.
|
||||||
|
struct {
|
||||||
|
struct AVAESCTR* aes_ctr;
|
||||||
|
unsigned int per_sample_iv_size; // Either 0, 8, or 16.
|
||||||
|
AVEncryptionInfo *default_encrypted_sample;
|
||||||
|
MOVEncryptionIndex *encryption_index;
|
||||||
|
} cenc;
|
||||||
|
} MOVStreamContext;
|
||||||
|
|
||||||
|
typedef struct MOVContext {
|
||||||
|
const AVClass *clazz; ///< class for private options
|
||||||
|
AVFormatContext *fc;
|
||||||
|
int time_scale;
|
||||||
|
int64_t duration; ///< duration of the longest track
|
||||||
|
int found_moov; ///< 'moov' atom has been found
|
||||||
|
int found_mdat; ///< 'mdat' atom has been found
|
||||||
|
int found_hdlr_mdta; ///< 'hdlr' atom with type 'mdta' has been found
|
||||||
|
int trak_index; ///< Index of the current 'trak'
|
||||||
|
char **meta_keys;
|
||||||
|
unsigned meta_keys_count;
|
||||||
|
DVDemuxContext *dv_demux;
|
||||||
|
AVFormatContext *dv_fctx;
|
||||||
|
int isom; ///< 1 if file is ISO Media (mp4/3gp)
|
||||||
|
MOVFragment fragment; ///< current fragment in moof atom
|
||||||
|
MOVTrackExt *trex_data;
|
||||||
|
unsigned trex_count;
|
||||||
|
int itunes_metadata; ///< metadata are itunes style
|
||||||
|
int handbrake_version;
|
||||||
|
int *chapter_tracks;
|
||||||
|
unsigned int nb_chapter_tracks;
|
||||||
|
int use_absolute_path;
|
||||||
|
int ignore_editlist;
|
||||||
|
int advanced_editlist;
|
||||||
|
int ignore_chapters;
|
||||||
|
int seek_individually;
|
||||||
|
int64_t next_root_atom; ///< offset of the next root atom
|
||||||
|
int export_all;
|
||||||
|
int export_xmp;
|
||||||
|
int *bitrates; ///< bitrates read before streams creation
|
||||||
|
int bitrates_count;
|
||||||
|
int moov_retry;
|
||||||
|
int use_mfra_for;
|
||||||
|
int has_looked_for_mfra;
|
||||||
|
MOVFragmentIndex frag_index;
|
||||||
|
int atom_depth;
|
||||||
|
unsigned int aax_mode; ///< 'aax' file has been detected
|
||||||
|
uint8_t file_key[20];
|
||||||
|
uint8_t file_iv[20];
|
||||||
|
void *activation_bytes;
|
||||||
|
int activation_bytes_size;
|
||||||
|
void *audible_fixed_key;
|
||||||
|
int audible_fixed_key_size;
|
||||||
|
struct AVAES *aes_decrypt;
|
||||||
|
uint8_t *decryption_key;
|
||||||
|
int decryption_key_len;
|
||||||
|
int enable_drefs;
|
||||||
|
int32_t movie_display_matrix[3][3]; ///< display matrix from mvhd
|
||||||
|
} MOVContext;
|
||||||
|
|
||||||
|
int ff_mp4_read_descr_len(AVIOContext *pb);
|
||||||
|
int ff_mp4_read_descr(AVFormatContext *fc, AVIOContext *pb, int *tag);
|
||||||
|
int ff_mp4_read_dec_config_descr(AVFormatContext *fc, AVStream *st, AVIOContext *pb);
|
||||||
|
void ff_mp4_parse_es_descr(AVIOContext *pb, int *es_id);
|
||||||
|
|
||||||
|
#define MP4ODescrTag 0x01
|
||||||
|
#define MP4IODescrTag 0x02
|
||||||
|
#define MP4ESDescrTag 0x03
|
||||||
|
#define MP4DecConfigDescrTag 0x04
|
||||||
|
#define MP4DecSpecificDescrTag 0x05
|
||||||
|
#define MP4SLDescrTag 0x06
|
||||||
|
|
||||||
|
#define MOV_TFHD_BASE_DATA_OFFSET 0x01
|
||||||
|
#define MOV_TFHD_STSD_ID 0x02
|
||||||
|
#define MOV_TFHD_DEFAULT_DURATION 0x08
|
||||||
|
#define MOV_TFHD_DEFAULT_SIZE 0x10
|
||||||
|
#define MOV_TFHD_DEFAULT_FLAGS 0x20
|
||||||
|
#define MOV_TFHD_DURATION_IS_EMPTY 0x010000
|
||||||
|
#define MOV_TFHD_DEFAULT_BASE_IS_MOOF 0x020000
|
||||||
|
|
||||||
|
#define MOV_TRUN_DATA_OFFSET 0x01
|
||||||
|
#define MOV_TRUN_FIRST_SAMPLE_FLAGS 0x04
|
||||||
|
#define MOV_TRUN_SAMPLE_DURATION 0x100
|
||||||
|
#define MOV_TRUN_SAMPLE_SIZE 0x200
|
||||||
|
#define MOV_TRUN_SAMPLE_FLAGS 0x400
|
||||||
|
#define MOV_TRUN_SAMPLE_CTS 0x800
|
||||||
|
|
||||||
|
#define MOV_FRAG_SAMPLE_FLAG_DEGRADATION_PRIORITY_MASK 0x0000ffff
|
||||||
|
#define MOV_FRAG_SAMPLE_FLAG_IS_NON_SYNC 0x00010000
|
||||||
|
#define MOV_FRAG_SAMPLE_FLAG_PADDING_MASK 0x000e0000
|
||||||
|
#define MOV_FRAG_SAMPLE_FLAG_REDUNDANCY_MASK 0x00300000
|
||||||
|
#define MOV_FRAG_SAMPLE_FLAG_DEPENDED_MASK 0x00c00000
|
||||||
|
#define MOV_FRAG_SAMPLE_FLAG_DEPENDS_MASK 0x03000000
|
||||||
|
|
||||||
|
#define MOV_FRAG_SAMPLE_FLAG_DEPENDS_NO 0x02000000
|
||||||
|
#define MOV_FRAG_SAMPLE_FLAG_DEPENDS_YES 0x01000000
|
||||||
|
|
||||||
|
#define MOV_TKHD_FLAG_ENABLED 0x0001
|
||||||
|
#define MOV_TKHD_FLAG_IN_MOVIE 0x0002
|
||||||
|
#define MOV_TKHD_FLAG_IN_PREVIEW 0x0004
|
||||||
|
#define MOV_TKHD_FLAG_IN_POSTER 0x0008
|
||||||
|
|
||||||
|
#define MOV_SAMPLE_DEPENDENCY_UNKNOWN 0x0
|
||||||
|
#define MOV_SAMPLE_DEPENDENCY_YES 0x1
|
||||||
|
#define MOV_SAMPLE_DEPENDENCY_NO 0x2
|
||||||
|
|
||||||
|
|
||||||
|
#define TAG_IS_AVCI(tag) \
|
||||||
|
((tag) == MKTAG('a', 'i', '5', 'p') || \
|
||||||
|
(tag) == MKTAG('a', 'i', '5', 'q') || \
|
||||||
|
(tag) == MKTAG('a', 'i', '5', '2') || \
|
||||||
|
(tag) == MKTAG('a', 'i', '5', '3') || \
|
||||||
|
(tag) == MKTAG('a', 'i', '5', '5') || \
|
||||||
|
(tag) == MKTAG('a', 'i', '5', '6') || \
|
||||||
|
(tag) == MKTAG('a', 'i', '1', 'p') || \
|
||||||
|
(tag) == MKTAG('a', 'i', '1', 'q') || \
|
||||||
|
(tag) == MKTAG('a', 'i', '1', '2') || \
|
||||||
|
(tag) == MKTAG('a', 'i', '1', '3') || \
|
||||||
|
(tag) == MKTAG('a', 'i', '1', '5') || \
|
||||||
|
(tag) == MKTAG('a', 'i', '1', '6') || \
|
||||||
|
(tag) == MKTAG('a', 'i', 'v', 'x') || \
|
||||||
|
(tag) == MKTAG('A', 'V', 'i', 'n'))
|
||||||
|
|
||||||
|
|
||||||
|
int ff_mov_read_esds(AVFormatContext *fc, AVIOContext *pb);
|
||||||
|
|
||||||
|
int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries);
|
||||||
|
void ff_mov_write_chan(AVIOContext *pb, int64_t channel_layout);
|
||||||
|
|
||||||
|
#define FF_MOV_FLAG_MFRA_AUTO -1
|
||||||
|
#define FF_MOV_FLAG_MFRA_DTS 1
|
||||||
|
#define FF_MOV_FLAG_MFRA_PTS 2
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* AVFORMAT_ISOM_H */
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
|
#include <libavformat/isom.h>
|
||||||
#include <libavutil/eval.h>
|
#include <libavutil/eval.h>
|
||||||
#include <libswscale/swscale.h>
|
#include <libswscale/swscale.h>
|
||||||
|
|
||||||
|
@ -67,11 +68,10 @@ typedef struct VideoInfo {
|
||||||
}
|
}
|
||||||
stream = nullptr;
|
stream = nullptr;
|
||||||
}
|
}
|
||||||
/*if (ioBuffer) { TODO memleak?
|
|
||||||
av_free(ioBuffer);
|
|
||||||
ioBuffer = nullptr;
|
|
||||||
}*/
|
|
||||||
if (ioContext != nullptr) {
|
if (ioContext != nullptr) {
|
||||||
|
if (ioContext->buffer) {
|
||||||
|
av_freep(&ioContext->buffer);
|
||||||
|
}
|
||||||
avio_context_free(&ioContext);
|
avio_context_free(&ioContext);
|
||||||
ioContext = nullptr;
|
ioContext = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -88,12 +88,14 @@ typedef struct VideoInfo {
|
||||||
|
|
||||||
video_stream_idx = -1;
|
video_stream_idx = -1;
|
||||||
video_stream = nullptr;
|
video_stream = nullptr;
|
||||||
|
audio_stream = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
AVFormatContext *fmt_ctx = nullptr;
|
AVFormatContext *fmt_ctx = nullptr;
|
||||||
char *src = nullptr;
|
char *src = nullptr;
|
||||||
int video_stream_idx = -1;
|
int video_stream_idx = -1;
|
||||||
AVStream *video_stream = nullptr;
|
AVStream *video_stream = nullptr;
|
||||||
|
AVStream *audio_stream = nullptr;
|
||||||
AVCodecContext *video_dec_ctx = nullptr;
|
AVCodecContext *video_dec_ctx = nullptr;
|
||||||
AVFrame *frame = nullptr;
|
AVFrame *frame = nullptr;
|
||||||
bool has_decoded_frames = false;
|
bool has_decoded_frames = false;
|
||||||
|
@ -248,6 +250,94 @@ int64_t seekCallback(void *opaque, int64_t offset, int whence) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum PARAM_NUM {
|
||||||
|
PARAM_NUM_IS_AVC = 0,
|
||||||
|
PARAM_NUM_WIDTH = 1,
|
||||||
|
PARAM_NUM_HEIGHT = 2,
|
||||||
|
PARAM_NUM_BITRATE = 3,
|
||||||
|
PARAM_NUM_DURATION = 4,
|
||||||
|
PARAM_NUM_AUDIO_FRAME_SIZE = 5,
|
||||||
|
PARAM_NUM_VIDEO_FRAME_SIZE = 6,
|
||||||
|
PARAM_NUM_FRAMERATE = 7,
|
||||||
|
PARAM_NUM_ROTATION = 8,
|
||||||
|
PARAM_NUM_COUNT = 9
|
||||||
|
};
|
||||||
|
|
||||||
|
void Java_org_telegram_ui_Components_AnimatedFileDrawable_getVideoInfo(JNIEnv *env, jclass clazz, jstring src, jintArray data) {
|
||||||
|
VideoInfo *info = new VideoInfo();
|
||||||
|
|
||||||
|
char const *srcString = env->GetStringUTFChars(src, 0);
|
||||||
|
size_t len = strlen(srcString);
|
||||||
|
info->src = new char[len + 1];
|
||||||
|
memcpy(info->src, srcString, len);
|
||||||
|
info->src[len] = '\0';
|
||||||
|
if (srcString != 0) {
|
||||||
|
env->ReleaseStringUTFChars(src, srcString);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
if ((ret = avformat_open_input(&info->fmt_ctx, info->src, NULL, NULL)) < 0) {
|
||||||
|
LOGE("can't open source file %s, %s", info->src, av_err2str(ret));
|
||||||
|
delete info;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ret = avformat_find_stream_info(info->fmt_ctx, NULL)) < 0) {
|
||||||
|
LOGE("can't find stream information %s, %s", info->src, av_err2str(ret));
|
||||||
|
delete info;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ret = av_find_best_stream(info->fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0)) >= 0) {
|
||||||
|
info->video_stream = info->fmt_ctx->streams[ret];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ret = av_find_best_stream(info->fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0)) >= 0) {
|
||||||
|
info->audio_stream = info->fmt_ctx->streams[ret];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->video_stream == nullptr) {
|
||||||
|
LOGE("can't find video stream in the input, aborting %s", info->src);
|
||||||
|
delete info;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
jint *dataArr = env->GetIntArrayElements(data, 0);
|
||||||
|
if (dataArr != nullptr) {
|
||||||
|
dataArr[PARAM_NUM_IS_AVC] = info->video_stream->codecpar->codec_id == AV_CODEC_ID_H264;
|
||||||
|
if (strstr(info->fmt_ctx->iformat->name, "mov") != 0 && dataArr[PARAM_NUM_IS_AVC]) {
|
||||||
|
MOVStreamContext *mov = (MOVStreamContext *) info->video_stream->priv_data;
|
||||||
|
dataArr[PARAM_NUM_VIDEO_FRAME_SIZE] = (jint) mov->data_size;
|
||||||
|
|
||||||
|
if (info->audio_stream != nullptr) {
|
||||||
|
mov = (MOVStreamContext *) info->audio_stream->priv_data;
|
||||||
|
dataArr[PARAM_NUM_AUDIO_FRAME_SIZE] = (jint) mov->data_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dataArr[PARAM_NUM_BITRATE] = (jint) info->video_stream->codecpar->bit_rate;
|
||||||
|
dataArr[PARAM_NUM_WIDTH] = info->video_stream->codecpar->width;
|
||||||
|
dataArr[PARAM_NUM_HEIGHT] = info->video_stream->codecpar->height;
|
||||||
|
AVDictionaryEntry *rotate_tag = av_dict_get(info->video_stream->metadata, "rotate", NULL, 0);
|
||||||
|
if (rotate_tag && *rotate_tag->value && strcmp(rotate_tag->value, "0")) {
|
||||||
|
char *tail;
|
||||||
|
dataArr[PARAM_NUM_ROTATION] = (jint) av_strtod(rotate_tag->value, &tail);
|
||||||
|
if (*tail) {
|
||||||
|
dataArr[PARAM_NUM_ROTATION] = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dataArr[PARAM_NUM_ROTATION] = 0;
|
||||||
|
}
|
||||||
|
if (info->video_stream->codecpar->codec_id == AV_CODEC_ID_H264) {
|
||||||
|
dataArr[PARAM_NUM_FRAMERATE] = (jint) av_q2d(info->video_stream->avg_frame_rate);
|
||||||
|
} else {
|
||||||
|
dataArr[PARAM_NUM_FRAMERATE] = (jint) av_q2d(info->video_stream->r_frame_rate);
|
||||||
|
}
|
||||||
|
dataArr[PARAM_NUM_DURATION] = (int32_t) (info->fmt_ctx->duration * 1000 / AV_TIME_BASE);
|
||||||
|
env->ReleaseIntArrayElements(data, dataArr, 0);
|
||||||
|
delete info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
jlong Java_org_telegram_ui_Components_AnimatedFileDrawable_createDecoder(JNIEnv *env, jclass clazz, jstring src, jintArray data, jint account, jlong streamFileSize, jobject stream) {
|
jlong Java_org_telegram_ui_Components_AnimatedFileDrawable_createDecoder(JNIEnv *env, jclass clazz, jstring src, jintArray data, jint account, jlong streamFileSize, jobject stream) {
|
||||||
VideoInfo *info = new VideoInfo();
|
VideoInfo *info = new VideoInfo();
|
||||||
|
|
||||||
|
@ -305,7 +395,7 @@ jlong Java_org_telegram_ui_Components_AnimatedFileDrawable_createDecoder(JNIEnv
|
||||||
info->video_stream = info->fmt_ctx->streams[info->video_stream_idx];
|
info->video_stream = info->fmt_ctx->streams[info->video_stream_idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info->video_stream <= 0) {
|
if (info->video_stream == nullptr) {
|
||||||
LOGE("can't find video stream in the input, aborting %s", info->src);
|
LOGE("can't find video stream in the input, aborting %s", info->src);
|
||||||
delete info;
|
delete info;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -442,12 +532,7 @@ void Java_org_telegram_ui_Components_AnimatedFileDrawable_seekToMs(JNIEnv *env,
|
||||||
}
|
}
|
||||||
if (got_frame) {
|
if (got_frame) {
|
||||||
if (info->frame->format == AV_PIX_FMT_YUV420P || info->frame->format == AV_PIX_FMT_BGRA || info->frame->format == AV_PIX_FMT_YUVJ420P) {
|
if (info->frame->format == AV_PIX_FMT_YUV420P || info->frame->format == AV_PIX_FMT_BGRA || info->frame->format == AV_PIX_FMT_YUVJ420P) {
|
||||||
int64_t pkt_pts;
|
int64_t pkt_pts = info->frame->best_effort_timestamp;
|
||||||
if (info->frame->pts != AV_NOPTS_VALUE) {
|
|
||||||
pkt_pts = info->frame->pts;
|
|
||||||
} else {
|
|
||||||
pkt_pts = info->frame->pkt_dts;
|
|
||||||
}
|
|
||||||
if (pkt_pts >= pts) {
|
if (pkt_pts >= pts) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -524,11 +609,7 @@ jint Java_org_telegram_ui_Components_AnimatedFileDrawable_getVideoFrame(JNIEnv *
|
||||||
if (dataArr != nullptr) {
|
if (dataArr != nullptr) {
|
||||||
wantedWidth = dataArr[0];
|
wantedWidth = dataArr[0];
|
||||||
wantedHeight = dataArr[1];
|
wantedHeight = dataArr[1];
|
||||||
if (info->frame->pts != AV_NOPTS_VALUE) {
|
dataArr[3] = (jint) (1000 * info->frame->best_effort_timestamp * av_q2d(info->video_stream->time_base));
|
||||||
dataArr[3] = (jint) (1000 * info->frame->pts * av_q2d(info->video_stream->time_base));
|
|
||||||
} else {
|
|
||||||
dataArr[3] = (jint) (1000 * info->frame->pkt_dts * av_q2d(info->video_stream->time_base));
|
|
||||||
}
|
|
||||||
env->ReleaseIntArrayElements(data, dataArr, 0);
|
env->ReleaseIntArrayElements(data, dataArr, 0);
|
||||||
} else {
|
} else {
|
||||||
AndroidBitmapInfo bitmapInfo;
|
AndroidBitmapInfo bitmapInfo;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit ce74c9216f599874571061f39c2dc31632b3004b
|
Subproject commit 78decc81bf25cf36ad1b4a9398aa11cb195db9c5
|
|
@ -123,9 +123,9 @@ extern "C" {
|
||||||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||||
** [sqlite_version()] and [sqlite_source_id()].
|
** [sqlite_version()] and [sqlite_source_id()].
|
||||||
*/
|
*/
|
||||||
#define SQLITE_VERSION "3.27.2"
|
#define SQLITE_VERSION "3.28.0"
|
||||||
#define SQLITE_VERSION_NUMBER 3027002
|
#define SQLITE_VERSION_NUMBER 3028000
|
||||||
#define SQLITE_SOURCE_ID "2019-02-25 16:06:06 bd49a8271d650fa89e446b42e513b595a717b9212c91dd384aab871fc1d0f6d7"
|
#define SQLITE_SOURCE_ID "2019-04-16 19:49:53 884b4b7e502b4e991677b53971277adfaf0a04a284f8e483e2553d0f83156b50"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Run-Time Library Version Numbers
|
** CAPI3REF: Run-Time Library Version Numbers
|
||||||
|
@ -189,6 +189,9 @@ SQLITE_API int sqlite3_libversion_number(void);
|
||||||
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
|
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
|
||||||
SQLITE_API int sqlite3_compileoption_used(const char *zOptName);
|
SQLITE_API int sqlite3_compileoption_used(const char *zOptName);
|
||||||
SQLITE_API const char *sqlite3_compileoption_get(int N);
|
SQLITE_API const char *sqlite3_compileoption_get(int N);
|
||||||
|
#else
|
||||||
|
# define sqlite3_compileoption_used(X) 0
|
||||||
|
# define sqlite3_compileoption_get(X) ((void*)0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2086,8 +2089,8 @@ struct sqlite3_mem_methods {
|
||||||
**
|
**
|
||||||
** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
|
** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
|
||||||
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
|
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
|
||||||
** <dd> ^This option is used to enable or disable the two-argument
|
** <dd> ^This option is used to enable or disable the
|
||||||
** version of the [fts3_tokenizer()] function which is part of the
|
** [fts3_tokenizer()] function which is part of the
|
||||||
** [FTS3] full-text search engine extension.
|
** [FTS3] full-text search engine extension.
|
||||||
** There should be two additional arguments.
|
** There should be two additional arguments.
|
||||||
** The first argument is an integer which is 0 to disable fts3_tokenizer() or
|
** The first argument is an integer which is 0 to disable fts3_tokenizer() or
|
||||||
|
@ -2199,6 +2202,17 @@ struct sqlite3_mem_methods {
|
||||||
** <li> Direct writes to [shadow tables].
|
** <li> Direct writes to [shadow tables].
|
||||||
** </ul>
|
** </ul>
|
||||||
** </dd>
|
** </dd>
|
||||||
|
**
|
||||||
|
** [[SQLITE_DBCONFIG_WRITABLE_SCHEMA]] <dt>SQLITE_DBCONFIG_WRITABLE_SCHEMA</dt>
|
||||||
|
** <dd>The SQLITE_DBCONFIG_WRITABLE_SCHEMA option activates or deactivates the
|
||||||
|
** "writable_schema" flag. This has the same effect and is logically equivalent
|
||||||
|
** to setting [PRAGMA writable_schema=ON] or [PRAGMA writable_schema=OFF].
|
||||||
|
** The first argument to this setting is an integer which is 0 to disable
|
||||||
|
** the writable_schema, positive to enable writable_schema, or negative to
|
||||||
|
** leave the setting unchanged. The second parameter is a pointer to an
|
||||||
|
** integer into which is written 0 or 1 to indicate whether the writable_schema
|
||||||
|
** is enabled or disabled following this call.
|
||||||
|
** </dd>
|
||||||
** </dl>
|
** </dl>
|
||||||
*/
|
*/
|
||||||
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
|
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
|
||||||
|
@ -2212,7 +2226,8 @@ struct sqlite3_mem_methods {
|
||||||
#define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */
|
#define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */
|
||||||
#define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */
|
#define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */
|
||||||
#define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */
|
#define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */
|
||||||
#define SQLITE_DBCONFIG_MAX 1010 /* Largest DBCONFIG */
|
#define SQLITE_DBCONFIG_WRITABLE_SCHEMA 1011 /* int int* */
|
||||||
|
#define SQLITE_DBCONFIG_MAX 1011 /* Largest DBCONFIG */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Enable Or Disable Extended Result Codes
|
** CAPI3REF: Enable Or Disable Extended Result Codes
|
||||||
|
@ -3894,6 +3909,18 @@ SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt);
|
||||||
*/
|
*/
|
||||||
SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
|
SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** CAPI3REF: Query The EXPLAIN Setting For A Prepared Statement
|
||||||
|
** METHOD: sqlite3_stmt
|
||||||
|
**
|
||||||
|
** ^The sqlite3_stmt_isexplain(S) interface returns 1 if the
|
||||||
|
** prepared statement S is an EXPLAIN statement, or 2 if the
|
||||||
|
** statement S is an EXPLAIN QUERY PLAN.
|
||||||
|
** ^The sqlite3_stmt_isexplain(S) interface returns 0 if S is
|
||||||
|
** an ordinary statement or a NULL pointer.
|
||||||
|
*/
|
||||||
|
SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Determine If A Prepared Statement Has Been Reset
|
** CAPI3REF: Determine If A Prepared Statement Has Been Reset
|
||||||
** METHOD: sqlite3_stmt
|
** METHOD: sqlite3_stmt
|
||||||
|
@ -4033,7 +4060,9 @@ typedef struct sqlite3_context sqlite3_context;
|
||||||
** ^The fifth argument to the BLOB and string binding interfaces
|
** ^The fifth argument to the BLOB and string binding interfaces
|
||||||
** is a destructor used to dispose of the BLOB or
|
** is a destructor used to dispose of the BLOB or
|
||||||
** string after SQLite has finished with it. ^The destructor is called
|
** string after SQLite has finished with it. ^The destructor is called
|
||||||
** to dispose of the BLOB or string even if the call to bind API fails.
|
** 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
|
** ^If the fifth argument is
|
||||||
** the special value [SQLITE_STATIC], then SQLite assumes that the
|
** the special value [SQLITE_STATIC], then SQLite assumes that the
|
||||||
** information is in static, unmanaged space and does not need to be freed.
|
** information is in static, unmanaged space and does not need to be freed.
|
||||||
|
@ -4950,6 +4979,8 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6
|
||||||
** <tr><td><b>sqlite3_value_nochange </b>
|
** <tr><td><b>sqlite3_value_nochange </b>
|
||||||
** <td>→ <td>True if the column is unchanged in an UPDATE
|
** <td>→ <td>True if the column is unchanged in an UPDATE
|
||||||
** against a virtual table.
|
** against a virtual table.
|
||||||
|
** <tr><td><b>sqlite3_value_frombind </b>
|
||||||
|
** <td>→ <td>True if value originated from a [bound parameter]
|
||||||
** </table></blockquote>
|
** </table></blockquote>
|
||||||
**
|
**
|
||||||
** <b>Details:</b>
|
** <b>Details:</b>
|
||||||
|
@ -5011,6 +5042,11 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6
|
||||||
** than within an [xUpdate] method call for an UPDATE statement, then
|
** than within an [xUpdate] method call for an UPDATE statement, then
|
||||||
** the return value is arbitrary and meaningless.
|
** the return value is arbitrary and meaningless.
|
||||||
**
|
**
|
||||||
|
** ^The sqlite3_value_frombind(X) interface returns non-zero if the
|
||||||
|
** value X originated from one of the [sqlite3_bind_int|sqlite3_bind()]
|
||||||
|
** interfaces. ^If X comes from an SQL literal value, or a table column,
|
||||||
|
** and expression, then sqlite3_value_frombind(X) returns zero.
|
||||||
|
**
|
||||||
** Please pay particular attention to the fact that the pointer returned
|
** Please pay particular attention to the fact that the pointer returned
|
||||||
** from [sqlite3_value_blob()], [sqlite3_value_text()], or
|
** from [sqlite3_value_blob()], [sqlite3_value_text()], or
|
||||||
** [sqlite3_value_text16()] can be invalidated by a subsequent call to
|
** [sqlite3_value_text16()] can be invalidated by a subsequent call to
|
||||||
|
@ -5056,6 +5092,7 @@ SQLITE_API int sqlite3_value_bytes16(sqlite3_value*);
|
||||||
SQLITE_API int sqlite3_value_type(sqlite3_value*);
|
SQLITE_API int sqlite3_value_type(sqlite3_value*);
|
||||||
SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
|
SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
|
||||||
SQLITE_API int sqlite3_value_nochange(sqlite3_value*);
|
SQLITE_API int sqlite3_value_nochange(sqlite3_value*);
|
||||||
|
SQLITE_API int sqlite3_value_frombind(sqlite3_value*);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Finding The Subtype Of SQL Values
|
** CAPI3REF: Finding The Subtype Of SQL Values
|
||||||
|
@ -5791,7 +5828,7 @@ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
|
||||||
** associated with database N of connection D. ^The main database file
|
** associated with database N of connection D. ^The main database file
|
||||||
** has the name "main". If there is no attached database N on the database
|
** has the name "main". If there is no attached database N on the database
|
||||||
** connection D, or if database N is a temporary or in-memory database, then
|
** connection D, or if database N is a temporary or in-memory database, then
|
||||||
** a NULL pointer is returned.
|
** this function will return either a NULL pointer or an empty string.
|
||||||
**
|
**
|
||||||
** ^The filename returned by this function is the output of the
|
** ^The filename returned by this function is the output of the
|
||||||
** xFullPathname method of the [VFS]. ^In other words, the filename
|
** xFullPathname method of the [VFS]. ^In other words, the filename
|
||||||
|
@ -10892,7 +10929,7 @@ SQLITE_API int sqlite3rebaser_configure(
|
||||||
** in size. This function allocates and populates a buffer with a copy
|
** in size. This function allocates and populates a buffer with a copy
|
||||||
** of the changeset rebased rebased according to the configuration of the
|
** of the changeset rebased rebased according to the configuration of the
|
||||||
** rebaser object passed as the first argument. If successful, (*ppOut)
|
** rebaser object passed as the first argument. If successful, (*ppOut)
|
||||||
** is set to point to the new buffer containing the rebased changset and
|
** is set to point to the new buffer containing the rebased changeset and
|
||||||
** (*pnOut) to its size in bytes and SQLITE_OK returned. It is the
|
** (*pnOut) to its size in bytes and SQLITE_OK returned. It is the
|
||||||
** responsibility of the caller to eventually free the new buffer using
|
** responsibility of the caller to eventually free the new buffer using
|
||||||
** sqlite3_free(). Otherwise, if an error occurs, (*ppOut) and (*pnOut)
|
** sqlite3_free(). Otherwise, if an error occurs, (*ppOut) and (*pnOut)
|
||||||
|
@ -11301,7 +11338,7 @@ struct Fts5PhraseIter {
|
||||||
** Save the pointer passed as the second argument as the extension functions
|
** Save the pointer passed as the second argument as the extension functions
|
||||||
** "auxiliary data". The pointer may then be retrieved by the current or any
|
** "auxiliary data". The pointer may then be retrieved by the current or any
|
||||||
** future invocation of the same fts5 extension function made as part of
|
** future invocation of the same fts5 extension function made as part of
|
||||||
** of the same MATCH query using the xGetAuxdata() API.
|
** the same MATCH query using the xGetAuxdata() API.
|
||||||
**
|
**
|
||||||
** Each extension function is allocated a single auxiliary data slot for
|
** Each extension function is allocated a single auxiliary data slot for
|
||||||
** each FTS query (MATCH expression). If the extension function is invoked
|
** each FTS query (MATCH expression). If the extension function is invoked
|
||||||
|
@ -11316,7 +11353,7 @@ struct Fts5PhraseIter {
|
||||||
** The xDelete callback, if one is specified, is also invoked on the
|
** The xDelete callback, if one is specified, is also invoked on the
|
||||||
** auxiliary data pointer after the FTS5 query has finished.
|
** auxiliary data pointer after the FTS5 query has finished.
|
||||||
**
|
**
|
||||||
** If an error (e.g. an OOM condition) occurs within this function, an
|
** If an error (e.g. an OOM condition) occurs within this function,
|
||||||
** the auxiliary data is set to NULL and an error code returned. If the
|
** the auxiliary data is set to NULL and an error code returned. If the
|
||||||
** xDelete parameter was not NULL, it is invoked on the auxiliary data
|
** xDelete parameter was not NULL, it is invoked on the auxiliary data
|
||||||
** pointer before returning.
|
** pointer before returning.
|
||||||
|
|
|
@ -206,6 +206,7 @@ void TL_config::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &
|
||||||
tmp_sessions = stream->readInt32(&error);
|
tmp_sessions = stream->readInt32(&error);
|
||||||
}
|
}
|
||||||
pinned_dialogs_count_max = stream->readInt32(&error);
|
pinned_dialogs_count_max = stream->readInt32(&error);
|
||||||
|
pinned_infolder_count_max = stream->readInt32(&error);
|
||||||
call_receive_timeout_ms = stream->readInt32(&error);
|
call_receive_timeout_ms = stream->readInt32(&error);
|
||||||
call_ring_timeout_ms = stream->readInt32(&error);
|
call_ring_timeout_ms = stream->readInt32(&error);
|
||||||
call_connect_timeout_ms = stream->readInt32(&error);
|
call_connect_timeout_ms = stream->readInt32(&error);
|
||||||
|
@ -277,6 +278,7 @@ void TL_config::serializeToStream(NativeByteBuffer *stream) {
|
||||||
stream->writeInt32(tmp_sessions);
|
stream->writeInt32(tmp_sessions);
|
||||||
}
|
}
|
||||||
stream->writeInt32(pinned_dialogs_count_max);
|
stream->writeInt32(pinned_dialogs_count_max);
|
||||||
|
stream->writeInt32(pinned_infolder_count_max);
|
||||||
stream->writeInt32(call_receive_timeout_ms);
|
stream->writeInt32(call_receive_timeout_ms);
|
||||||
stream->writeInt32(call_ring_timeout_ms);
|
stream->writeInt32(call_ring_timeout_ms);
|
||||||
stream->writeInt32(call_connect_timeout_ms);
|
stream->writeInt32(call_connect_timeout_ms);
|
||||||
|
@ -570,14 +572,8 @@ void TL_userStatusRecently::serializeToStream(NativeByteBuffer *stream) {
|
||||||
FileLocation *FileLocation::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
FileLocation *FileLocation::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||||
FileLocation *result = nullptr;
|
FileLocation *result = nullptr;
|
||||||
switch (constructor) {
|
switch (constructor) {
|
||||||
case 0x91d11eb:
|
case 0xbc7fc6cd:
|
||||||
result = new TL_fileLocation();
|
result = new TL_fileLocationToBeDeprecated();
|
||||||
break;
|
|
||||||
case 0x7c596b46:
|
|
||||||
result = new TL_fileLocationUnavailable();
|
|
||||||
break;
|
|
||||||
case 0x55555554:
|
|
||||||
result = new TL_fileEncryptedLocation();
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error = true;
|
error = true;
|
||||||
|
@ -588,53 +584,15 @@ FileLocation *FileLocation::TLdeserialize(NativeByteBuffer *stream, uint32_t con
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TL_fileLocation::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
void TL_fileLocationToBeDeprecated::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
||||||
dc_id = stream->readInt32(&error);
|
|
||||||
volume_id = stream->readInt64(&error);
|
volume_id = stream->readInt64(&error);
|
||||||
local_id = stream->readInt32(&error);
|
local_id = stream->readInt32(&error);
|
||||||
secret = stream->readInt64(&error);
|
|
||||||
file_reference = std::unique_ptr<ByteArray>(stream->readByteArray(&error));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TL_fileLocation::serializeToStream(NativeByteBuffer *stream) {
|
void TL_fileLocationToBeDeprecated::serializeToStream(NativeByteBuffer *stream) {
|
||||||
stream->writeInt32(constructor);
|
|
||||||
stream->writeInt32(dc_id);
|
|
||||||
stream->writeInt64(volume_id);
|
|
||||||
stream->writeInt32(local_id);
|
|
||||||
stream->writeInt64(secret);
|
|
||||||
stream->writeByteArray(file_reference.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_fileLocationUnavailable::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
|
||||||
volume_id = stream->readInt64(&error);
|
|
||||||
local_id = stream->readInt32(&error);
|
|
||||||
secret = stream->readInt64(&error);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_fileLocationUnavailable::serializeToStream(NativeByteBuffer *stream) {
|
|
||||||
stream->writeInt32(constructor);
|
stream->writeInt32(constructor);
|
||||||
stream->writeInt64(volume_id);
|
stream->writeInt64(volume_id);
|
||||||
stream->writeInt32(local_id);
|
stream->writeInt32(local_id);
|
||||||
stream->writeInt64(secret);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_fileEncryptedLocation::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
|
||||||
dc_id = stream->readInt32(&error);
|
|
||||||
volume_id = stream->readInt64(&error);
|
|
||||||
local_id = stream->readInt32(&error);
|
|
||||||
secret = stream->readInt64(&error);
|
|
||||||
key = std::unique_ptr<ByteArray>(stream->readByteArray(&error));
|
|
||||||
iv = std::unique_ptr<ByteArray>(stream->readByteArray(&error));
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_fileEncryptedLocation::serializeToStream(NativeByteBuffer *stream) {
|
|
||||||
stream->writeInt32(constructor);
|
|
||||||
stream->writeInt32(dc_id);
|
|
||||||
stream->writeInt64(volume_id);
|
|
||||||
stream->writeInt32(local_id);
|
|
||||||
stream->writeInt64(secret);
|
|
||||||
stream->writeByteArray(key.get());
|
|
||||||
stream->writeByteArray(iv.get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UserProfilePhoto *UserProfilePhoto::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
UserProfilePhoto *UserProfilePhoto::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||||
|
@ -643,7 +601,7 @@ UserProfilePhoto *UserProfilePhoto::TLdeserialize(NativeByteBuffer *stream, uint
|
||||||
case 0x4f11bae1:
|
case 0x4f11bae1:
|
||||||
result = new TL_userProfilePhotoEmpty();
|
result = new TL_userProfilePhotoEmpty();
|
||||||
break;
|
break;
|
||||||
case 0xd559d8c8:
|
case 0xecd75d8c:
|
||||||
result = new TL_userProfilePhoto();
|
result = new TL_userProfilePhoto();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -663,6 +621,7 @@ void TL_userProfilePhoto::readParams(NativeByteBuffer *stream, int32_t instanceN
|
||||||
photo_id = stream->readInt64(&error);
|
photo_id = stream->readInt64(&error);
|
||||||
photo_small = std::unique_ptr<FileLocation>(FileLocation::TLdeserialize(stream, stream->readUint32(&error), instanceNum, error));
|
photo_small = std::unique_ptr<FileLocation>(FileLocation::TLdeserialize(stream, stream->readUint32(&error), instanceNum, error));
|
||||||
photo_big = std::unique_ptr<FileLocation>(FileLocation::TLdeserialize(stream, stream->readUint32(&error), instanceNum, error));
|
photo_big = std::unique_ptr<FileLocation>(FileLocation::TLdeserialize(stream, stream->readUint32(&error), instanceNum, error));
|
||||||
|
dc_id = stream->readInt32(&error);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TL_userProfilePhoto::serializeToStream(NativeByteBuffer *stream) {
|
void TL_userProfilePhoto::serializeToStream(NativeByteBuffer *stream) {
|
||||||
|
@ -670,263 +629,9 @@ void TL_userProfilePhoto::serializeToStream(NativeByteBuffer *stream) {
|
||||||
stream->writeInt64(photo_id);
|
stream->writeInt64(photo_id);
|
||||||
photo_small->serializeToStream(stream);
|
photo_small->serializeToStream(stream);
|
||||||
photo_big->serializeToStream(stream);
|
photo_big->serializeToStream(stream);
|
||||||
}
|
stream->writeInt32(dc_id);
|
||||||
|
|
||||||
auth_SentCode *auth_SentCode::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
|
||||||
auth_SentCode *result = nullptr;
|
|
||||||
switch (constructor) {
|
|
||||||
case 0xe325edcf:
|
|
||||||
result = new TL_auth_sentAppCode();
|
|
||||||
break;
|
|
||||||
case 0xefed51d9:
|
|
||||||
result = new TL_auth_sentCode();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
error = true;
|
|
||||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in auth_SentCode", constructor);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
result->readParams(stream, instanceNum, error);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_auth_sentAppCode::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
|
||||||
phone_registered = stream->readBool(&error);
|
|
||||||
phone_code_hash = stream->readString(&error);
|
|
||||||
send_call_timeout = stream->readInt32(&error);
|
|
||||||
is_password = stream->readBool(&error);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_auth_sentAppCode::serializeToStream(NativeByteBuffer *stream) {
|
|
||||||
stream->writeInt32(constructor);
|
|
||||||
stream->writeBool(phone_registered);
|
|
||||||
stream->writeString(phone_code_hash);
|
|
||||||
stream->writeInt32(send_call_timeout);
|
|
||||||
stream->writeBool(is_password);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_auth_sentCode::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
|
||||||
phone_registered = stream->readBool(&error);
|
|
||||||
phone_code_hash = stream->readString(&error);
|
|
||||||
send_call_timeout = stream->readInt32(&error);
|
|
||||||
is_password = stream->readBool(&error);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_auth_sentCode::serializeToStream(NativeByteBuffer *stream) {
|
|
||||||
stream->writeInt32(constructor);
|
|
||||||
stream->writeBool(phone_registered);
|
|
||||||
stream->writeString(phone_code_hash);
|
|
||||||
stream->writeInt32(send_call_timeout);
|
|
||||||
stream->writeBool(is_password);
|
|
||||||
}
|
|
||||||
|
|
||||||
TLObject *TL_auth_sendCode::deserializeResponse(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
|
||||||
return auth_SentCode::TLdeserialize(stream, constructor, instanceNum, error);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_auth_sendCode::serializeToStream(NativeByteBuffer *stream) {
|
|
||||||
stream->writeInt32(constructor);
|
|
||||||
stream->writeString(phone_number);
|
|
||||||
stream->writeInt32(sms_type);
|
|
||||||
stream->writeInt32(api_id);
|
|
||||||
stream->writeString(api_hash);
|
|
||||||
stream->writeString(lang_code);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TL_updatesTooLong::serializeToStream(NativeByteBuffer *stream) {
|
void TL_updatesTooLong::serializeToStream(NativeByteBuffer *stream) {
|
||||||
stream->writeInt32(constructor);
|
stream->writeInt32(constructor);
|
||||||
}
|
}
|
||||||
|
|
||||||
TL_upload_file *TL_upload_file::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
|
||||||
if (TL_upload_file::constructor != constructor) {
|
|
||||||
error = true;
|
|
||||||
FileLog::e("can't parse magic %x in TL_upload_file", constructor);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
TL_upload_file *result = new TL_upload_file();
|
|
||||||
result->readParams(stream, instanceNum, error);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
TL_upload_file::~TL_upload_file() {
|
|
||||||
if (bytes != nullptr) {
|
|
||||||
bytes->reuse();
|
|
||||||
bytes = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_upload_file::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
|
||||||
type = std::unique_ptr<storage_FileType>(storage_FileType::TLdeserialize(stream, stream->readUint32(&error), instanceNum, error));
|
|
||||||
mtime = stream->readInt32(&error);
|
|
||||||
bytes = stream->readByteBuffer(true, &error);
|
|
||||||
}
|
|
||||||
|
|
||||||
InputFileLocation *InputFileLocation::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
|
||||||
InputFileLocation *result = nullptr;
|
|
||||||
switch (constructor) {
|
|
||||||
case 0x430f0724:
|
|
||||||
result = new TL_inputDocumentFileLocation();
|
|
||||||
break;
|
|
||||||
case 0x14637196:
|
|
||||||
result = new TL_inputFileLocation();
|
|
||||||
break;
|
|
||||||
case 0xf5235d55:
|
|
||||||
result = new TL_inputEncryptedFileLocation();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
error = true;
|
|
||||||
FileLog::e("can't parse magic %x in InputFileLocation", constructor);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
result->readParams(stream, instanceNum, error);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_inputDocumentFileLocation::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
|
||||||
id = stream->readInt64(&error);
|
|
||||||
access_hash = stream->readInt64(&error);
|
|
||||||
version = stream->readInt32(&error);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_inputDocumentFileLocation::serializeToStream(NativeByteBuffer *stream) {
|
|
||||||
stream->writeInt32(constructor);
|
|
||||||
stream->writeInt64(id);
|
|
||||||
stream->writeInt64(access_hash);
|
|
||||||
stream->writeInt32(version);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_inputFileLocation::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
|
||||||
volume_id = stream->readInt64(&error);
|
|
||||||
local_id = stream->readInt32(&error);
|
|
||||||
secret = stream->readInt64(&error);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_inputFileLocation::serializeToStream(NativeByteBuffer *stream) {
|
|
||||||
stream->writeInt32(constructor);
|
|
||||||
stream->writeInt64(volume_id);
|
|
||||||
stream->writeInt32(local_id);
|
|
||||||
stream->writeInt64(secret);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_inputEncryptedFileLocation::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
|
||||||
id = stream->readInt64(&error);
|
|
||||||
access_hash = stream->readInt64(&error);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_inputEncryptedFileLocation::serializeToStream(NativeByteBuffer *stream) {
|
|
||||||
stream->writeInt32(constructor);
|
|
||||||
stream->writeInt64(id);
|
|
||||||
stream->writeInt64(access_hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
storage_FileType *storage_FileType::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
|
||||||
storage_FileType *result = nullptr;
|
|
||||||
switch (constructor) {
|
|
||||||
case 0xaa963b05:
|
|
||||||
result = new TL_storage_fileUnknown();
|
|
||||||
break;
|
|
||||||
case 0xb3cea0e4:
|
|
||||||
result = new TL_storage_fileMp4();
|
|
||||||
break;
|
|
||||||
case 0x1081464c:
|
|
||||||
result = new TL_storage_fileWebp();
|
|
||||||
break;
|
|
||||||
case 0xa4f63c0:
|
|
||||||
result = new TL_storage_filePng();
|
|
||||||
break;
|
|
||||||
case 0xcae1aadf:
|
|
||||||
result = new TL_storage_fileGif();
|
|
||||||
break;
|
|
||||||
case 0xae1e508d:
|
|
||||||
result = new TL_storage_filePdf();
|
|
||||||
break;
|
|
||||||
case 0x528a0677:
|
|
||||||
result = new TL_storage_fileMp3();
|
|
||||||
break;
|
|
||||||
case 0x7efe0e:
|
|
||||||
result = new TL_storage_fileJpeg();
|
|
||||||
break;
|
|
||||||
case 0x4b09ebbc:
|
|
||||||
result = new TL_storage_fileMov();
|
|
||||||
break;
|
|
||||||
case 0x40bc6f52:
|
|
||||||
result = new TL_storage_filePartial();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
error = true;
|
|
||||||
FileLog::e("can't parse magic %x in storage_FileType", constructor);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
result->readParams(stream, instanceNum, error);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_storage_fileUnknown::serializeToStream(NativeByteBuffer *stream) {
|
|
||||||
stream->writeInt32(constructor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_storage_fileMp4::serializeToStream(NativeByteBuffer *stream) {
|
|
||||||
stream->writeInt32(constructor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_storage_fileWebp::serializeToStream(NativeByteBuffer *stream) {
|
|
||||||
stream->writeInt32(constructor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_storage_filePng::serializeToStream(NativeByteBuffer *stream) {
|
|
||||||
stream->writeInt32(constructor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_storage_fileGif::serializeToStream(NativeByteBuffer *stream) {
|
|
||||||
stream->writeInt32(constructor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_storage_filePdf::serializeToStream(NativeByteBuffer *stream) {
|
|
||||||
stream->writeInt32(constructor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_storage_fileMp3::serializeToStream(NativeByteBuffer *stream) {
|
|
||||||
stream->writeInt32(constructor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_storage_fileJpeg::serializeToStream(NativeByteBuffer *stream) {
|
|
||||||
stream->writeInt32(constructor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_storage_fileMov::serializeToStream(NativeByteBuffer *stream) {
|
|
||||||
stream->writeInt32(constructor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_storage_filePartial::serializeToStream(NativeByteBuffer *stream) {
|
|
||||||
stream->writeInt32(constructor);
|
|
||||||
}
|
|
||||||
|
|
||||||
TLObject *TL_upload_saveFilePart::deserializeResponse(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
|
||||||
return Bool::TLdeserialize(stream, constructor, instanceNum, error);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_upload_saveFilePart::serializeToStream(NativeByteBuffer *stream) {
|
|
||||||
stream->writeInt32(constructor);
|
|
||||||
stream->writeInt64(file_id);
|
|
||||||
stream->writeInt32(file_part);
|
|
||||||
stream->writeByteArray(bytes.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TL_upload_saveFilePart::isNeedLayer() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
TLObject *TL_upload_getFile::deserializeResponse(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
|
||||||
return TL_upload_file::TLdeserialize(stream, constructor, instanceNum, error);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TL_upload_getFile::serializeToStream(NativeByteBuffer *stream) {
|
|
||||||
stream->writeInt32(constructor);
|
|
||||||
location->serializeToStream(stream);
|
|
||||||
stream->writeInt32(offset);
|
|
||||||
stream->writeInt32(limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TL_upload_getFile::isNeedLayer() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
|
@ -98,7 +98,7 @@ public:
|
||||||
class TL_config : public TLObject {
|
class TL_config : public TLObject {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static const uint32_t constructor = 0xe6ca25f6;
|
static const uint32_t constructor = 0x330b4067;
|
||||||
|
|
||||||
int32_t flags;
|
int32_t flags;
|
||||||
int32_t date;
|
int32_t date;
|
||||||
|
@ -128,6 +128,7 @@ public:
|
||||||
int32_t channels_read_media_period;
|
int32_t channels_read_media_period;
|
||||||
int32_t tmp_sessions;
|
int32_t tmp_sessions;
|
||||||
int32_t pinned_dialogs_count_max;
|
int32_t pinned_dialogs_count_max;
|
||||||
|
int32_t pinned_infolder_count_max;
|
||||||
int32_t call_receive_timeout_ms;
|
int32_t call_receive_timeout_ms;
|
||||||
int32_t call_ring_timeout_ms;
|
int32_t call_ring_timeout_ms;
|
||||||
int32_t call_connect_timeout_ms;
|
int32_t call_connect_timeout_ms;
|
||||||
|
@ -234,39 +235,16 @@ public:
|
||||||
class FileLocation : public TLObject {
|
class FileLocation : public TLObject {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int32_t dc_id;
|
|
||||||
int64_t volume_id;
|
int64_t volume_id;
|
||||||
int32_t local_id;
|
int32_t local_id;
|
||||||
int64_t secret;
|
|
||||||
std::unique_ptr<ByteArray> file_reference;
|
|
||||||
std::unique_ptr<ByteArray> key;
|
|
||||||
std::unique_ptr<ByteArray> iv;
|
|
||||||
|
|
||||||
static FileLocation *TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error);
|
static FileLocation *TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error);
|
||||||
};
|
};
|
||||||
|
|
||||||
class TL_fileLocation : public FileLocation {
|
class TL_fileLocationToBeDeprecated : public FileLocation {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static const uint32_t constructor = 0x91d11eb;
|
static const uint32_t constructor = 0xbc7fc6cd;
|
||||||
|
|
||||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
|
||||||
void serializeToStream(NativeByteBuffer *stream);
|
|
||||||
};
|
|
||||||
|
|
||||||
class TL_fileEncryptedLocation : public FileLocation {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const uint32_t constructor = 0x55555554;
|
|
||||||
|
|
||||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
|
||||||
void serializeToStream(NativeByteBuffer *stream);
|
|
||||||
};
|
|
||||||
|
|
||||||
class TL_fileLocationUnavailable : public FileLocation {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const uint32_t constructor = 0x7c596b46;
|
|
||||||
|
|
||||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
||||||
void serializeToStream(NativeByteBuffer *stream);
|
void serializeToStream(NativeByteBuffer *stream);
|
||||||
|
@ -278,6 +256,7 @@ public:
|
||||||
int64_t photo_id;
|
int64_t photo_id;
|
||||||
std::unique_ptr<FileLocation> photo_small;
|
std::unique_ptr<FileLocation> photo_small;
|
||||||
std::unique_ptr<FileLocation> photo_big;
|
std::unique_ptr<FileLocation> photo_big;
|
||||||
|
int32_t dc_id;
|
||||||
|
|
||||||
static UserProfilePhoto *TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error);
|
static UserProfilePhoto *TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error);
|
||||||
};
|
};
|
||||||
|
@ -293,7 +272,7 @@ public:
|
||||||
class TL_userProfilePhoto : public UserProfilePhoto {
|
class TL_userProfilePhoto : public UserProfilePhoto {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static const uint32_t constructor = 0xd559d8c8;
|
static const uint32_t constructor = 0xecd75d8c;
|
||||||
|
|
||||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
||||||
void serializeToStream(NativeByteBuffer *stream);
|
void serializeToStream(NativeByteBuffer *stream);
|
||||||
|
@ -387,50 +366,6 @@ public:
|
||||||
void serializeToStream(NativeByteBuffer *stream);
|
void serializeToStream(NativeByteBuffer *stream);
|
||||||
};
|
};
|
||||||
|
|
||||||
class auth_SentCode : public TLObject {
|
|
||||||
|
|
||||||
public:
|
|
||||||
bool phone_registered;
|
|
||||||
std::string phone_code_hash;
|
|
||||||
int32_t send_call_timeout;
|
|
||||||
bool is_password;
|
|
||||||
|
|
||||||
static auth_SentCode *TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error);
|
|
||||||
};
|
|
||||||
|
|
||||||
class TL_auth_sentAppCode : public auth_SentCode {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const uint32_t constructor = 0xe325edcf;
|
|
||||||
|
|
||||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
|
||||||
void serializeToStream(NativeByteBuffer *stream);
|
|
||||||
};
|
|
||||||
|
|
||||||
class TL_auth_sentCode : public auth_SentCode {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const uint32_t constructor = 0xefed51d9;
|
|
||||||
|
|
||||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
|
||||||
void serializeToStream(NativeByteBuffer *stream);
|
|
||||||
};
|
|
||||||
|
|
||||||
class TL_auth_sendCode : public TLObject {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const uint32_t constructor = 0x768d5f4d;
|
|
||||||
|
|
||||||
std::string phone_number;
|
|
||||||
int32_t sms_type;
|
|
||||||
int32_t api_id;
|
|
||||||
std::string api_hash;
|
|
||||||
std::string lang_code;
|
|
||||||
|
|
||||||
TLObject *deserializeResponse(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error);
|
|
||||||
void serializeToStream(NativeByteBuffer *stream);
|
|
||||||
};
|
|
||||||
|
|
||||||
class TL_updatesTooLong : public TLObject {
|
class TL_updatesTooLong : public TLObject {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -439,173 +374,4 @@ public:
|
||||||
void serializeToStream(NativeByteBuffer *stream);
|
void serializeToStream(NativeByteBuffer *stream);
|
||||||
};
|
};
|
||||||
|
|
||||||
class storage_FileType : public TLObject {
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
static storage_FileType *TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error);
|
|
||||||
};
|
|
||||||
|
|
||||||
class TL_storage_fileUnknown : public storage_FileType {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const uint32_t constructor = 0xaa963b05;
|
|
||||||
|
|
||||||
void serializeToStream(NativeByteBuffer *stream);
|
|
||||||
};
|
|
||||||
|
|
||||||
class TL_storage_fileMp4 : public storage_FileType {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const uint32_t constructor = 0xb3cea0e4;
|
|
||||||
|
|
||||||
void serializeToStream(NativeByteBuffer *stream);
|
|
||||||
};
|
|
||||||
|
|
||||||
class TL_storage_fileWebp : public storage_FileType {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const uint32_t constructor = 0x1081464c;
|
|
||||||
|
|
||||||
void serializeToStream(NativeByteBuffer *stream);
|
|
||||||
};
|
|
||||||
|
|
||||||
class TL_storage_filePng : public storage_FileType {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const uint32_t constructor = 0xa4f63c0;
|
|
||||||
|
|
||||||
void serializeToStream(NativeByteBuffer *stream);
|
|
||||||
};
|
|
||||||
|
|
||||||
class TL_storage_fileGif : public storage_FileType {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const uint32_t constructor = 0xcae1aadf;
|
|
||||||
|
|
||||||
void serializeToStream(NativeByteBuffer *stream);
|
|
||||||
};
|
|
||||||
|
|
||||||
class TL_storage_filePdf : public storage_FileType {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const uint32_t constructor = 0xae1e508d;
|
|
||||||
|
|
||||||
void serializeToStream(NativeByteBuffer *stream);
|
|
||||||
};
|
|
||||||
|
|
||||||
class TL_storage_fileMp3 : public storage_FileType {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const uint32_t constructor = 0x528a0677;
|
|
||||||
|
|
||||||
void serializeToStream(NativeByteBuffer *stream);
|
|
||||||
};
|
|
||||||
|
|
||||||
class TL_storage_fileJpeg : public storage_FileType {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const uint32_t constructor = 0x7efe0e;
|
|
||||||
|
|
||||||
void serializeToStream(NativeByteBuffer *stream);
|
|
||||||
};
|
|
||||||
|
|
||||||
class TL_storage_fileMov : public storage_FileType {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const uint32_t constructor = 0x4b09ebbc;
|
|
||||||
|
|
||||||
void serializeToStream(NativeByteBuffer *stream);
|
|
||||||
};
|
|
||||||
|
|
||||||
class TL_storage_filePartial : public storage_FileType {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const uint32_t constructor = 0x40bc6f52;
|
|
||||||
|
|
||||||
void serializeToStream(NativeByteBuffer *stream);
|
|
||||||
};
|
|
||||||
|
|
||||||
class InputFileLocation : public TLObject {
|
|
||||||
|
|
||||||
public:
|
|
||||||
int64_t id;
|
|
||||||
int64_t access_hash;
|
|
||||||
int32_t version;
|
|
||||||
int64_t volume_id;
|
|
||||||
int32_t local_id;
|
|
||||||
int64_t secret;
|
|
||||||
|
|
||||||
static InputFileLocation *TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error);
|
|
||||||
};
|
|
||||||
|
|
||||||
class TL_inputDocumentFileLocation : public InputFileLocation {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const uint32_t constructor = 0x430f0724;
|
|
||||||
|
|
||||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
|
||||||
void serializeToStream(NativeByteBuffer *stream);
|
|
||||||
};
|
|
||||||
|
|
||||||
class TL_inputFileLocation : public InputFileLocation {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const uint32_t constructor = 0x14637196;
|
|
||||||
|
|
||||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
|
||||||
void serializeToStream(NativeByteBuffer *stream);
|
|
||||||
};
|
|
||||||
|
|
||||||
class TL_inputEncryptedFileLocation : public InputFileLocation {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const uint32_t constructor = 0xf5235d55;
|
|
||||||
|
|
||||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
|
||||||
void serializeToStream(NativeByteBuffer *stream);
|
|
||||||
};
|
|
||||||
|
|
||||||
class TL_upload_saveFilePart : public TLObject {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const uint32_t constructor = 0xb304a621;
|
|
||||||
|
|
||||||
int64_t file_id;
|
|
||||||
int32_t file_part;
|
|
||||||
std::unique_ptr<ByteArray> bytes;
|
|
||||||
|
|
||||||
bool isNeedLayer();
|
|
||||||
TLObject *deserializeResponse(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error);
|
|
||||||
void serializeToStream(NativeByteBuffer *stream);
|
|
||||||
};
|
|
||||||
|
|
||||||
class TL_upload_file : public TLObject {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const uint32_t constructor = 0x96a18d5;
|
|
||||||
|
|
||||||
std::unique_ptr<storage_FileType> type;
|
|
||||||
int32_t mtime;
|
|
||||||
NativeByteBuffer *bytes = nullptr;
|
|
||||||
|
|
||||||
~TL_upload_file();
|
|
||||||
static TL_upload_file *TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error);
|
|
||||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
|
||||||
};
|
|
||||||
|
|
||||||
class TL_upload_getFile : public TLObject {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const uint32_t constructor = 0xe3a6cfb5;
|
|
||||||
|
|
||||||
InputFileLocation *location;
|
|
||||||
int32_t offset;
|
|
||||||
int32_t limit;
|
|
||||||
|
|
||||||
bool isNeedLayer();
|
|
||||||
TLObject *deserializeResponse(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error);
|
|
||||||
void serializeToStream(NativeByteBuffer *stream);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -111,7 +111,7 @@ void BuffersStorage::reuseFreeBuffer(NativeByteBuffer *buffer) {
|
||||||
if (arrayToReuse->size() < maxCount) {
|
if (arrayToReuse->size() < maxCount) {
|
||||||
arrayToReuse->push_back(buffer);
|
arrayToReuse->push_back(buffer);
|
||||||
} else {
|
} else {
|
||||||
if (LOGS_ENABLED) DEBUG_D("too more %d buffers", capacity);
|
if (LOGS_ENABLED) DEBUG_D("too much %d buffers", capacity);
|
||||||
delete buffer;
|
delete buffer;
|
||||||
}
|
}
|
||||||
if (isThreadSafe) {
|
if (isThreadSafe) {
|
||||||
|
|
|
@ -288,6 +288,7 @@ void Connection::connect() {
|
||||||
if (connectionState == TcpConnectionStageConnected || connectionState == TcpConnectionStageConnecting) {
|
if (connectionState == TcpConnectionStageConnected || connectionState == TcpConnectionStageConnecting) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
connectionInProcess = true;
|
||||||
connectionState = TcpConnectionStageConnecting;
|
connectionState = TcpConnectionStageConnecting;
|
||||||
isMediaConnection = false;
|
isMediaConnection = false;
|
||||||
uint32_t ipv6 = ConnectionsManager::getInstance(currentDatacenter->instanceNum).isIpv6Enabled() ? TcpAddressFlagIpv6 : 0;
|
uint32_t ipv6 = ConnectionsManager::getInstance(currentDatacenter->instanceNum).isIpv6Enabled() ? TcpAddressFlagIpv6 : 0;
|
||||||
|
@ -369,6 +370,7 @@ void Connection::connect() {
|
||||||
setTimeout(12);
|
setTimeout(12);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
connectionInProcess = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::reconnect() {
|
void Connection::reconnect() {
|
||||||
|
@ -643,7 +645,7 @@ inline void Connection::encryptKeyWithSecret(uint8_t *bytes, uint8_t secretType)
|
||||||
SHA256_Final(bytes, &sha256Ctx);
|
SHA256_Final(bytes, &sha256Ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::onDisconnected(int32_t reason, int32_t error) {
|
void Connection::onDisconnectedInternal(int32_t reason, int32_t error) {
|
||||||
reconnectTimer->stop();
|
reconnectTimer->stop();
|
||||||
if (LOGS_ENABLED) DEBUG_D("connection(%p, account%u, dc%u, type %d) disconnected with reason %d", this, currentDatacenter->instanceNum, currentDatacenter->getDatacenterId(), connectionType, reason);
|
if (LOGS_ENABLED) DEBUG_D("connection(%p, account%u, dc%u, type %d) disconnected with reason %d", this, currentDatacenter->instanceNum, currentDatacenter->getDatacenterId(), connectionType, reason);
|
||||||
bool switchToNextPort = reason == 2 && wasConnected && (!hasSomeDataSinceLastConnect || currentDatacenter->isCustomPort(currentAddressFlags)) || forceNextPort;
|
bool switchToNextPort = reason == 2 && wasConnected && (!hasSomeDataSinceLastConnect || currentDatacenter->isCustomPort(currentAddressFlags)) || forceNextPort;
|
||||||
|
@ -677,7 +679,7 @@ void Connection::onDisconnected(int32_t reason, int32_t error) {
|
||||||
willRetryConnectCount = 1;
|
willRetryConnectCount = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ConnectionsManager::getInstance(currentDatacenter->instanceNum).isNetworkAvailable()) {
|
if (ConnectionsManager::getInstance(currentDatacenter->instanceNum).isNetworkAvailable() && connectionType != ConnectionTypeProxy) {
|
||||||
isTryingNextPort = true;
|
isTryingNextPort = true;
|
||||||
if (failedConnectionCount > willRetryConnectCount || switchToNextPort) {
|
if (failedConnectionCount > willRetryConnectCount || switchToNextPort) {
|
||||||
currentDatacenter->nextAddressOrPort(currentAddressFlags);
|
currentDatacenter->nextAddressOrPort(currentAddressFlags);
|
||||||
|
@ -706,6 +708,16 @@ void Connection::onDisconnected(int32_t reason, int32_t error) {
|
||||||
usefullData = false;
|
usefullData = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Connection::onDisconnected(int32_t reason, int32_t error) {
|
||||||
|
if (connectionInProcess) {
|
||||||
|
ConnectionsManager::getInstance(currentDatacenter->instanceNum).scheduleTask([&, reason, error] {
|
||||||
|
onDisconnectedInternal(reason, error);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
onDisconnectedInternal(reason, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Connection::onConnected() {
|
void Connection::onConnected() {
|
||||||
connectionState = TcpConnectionStageConnected;
|
connectionState = TcpConnectionStageConnected;
|
||||||
connectionToken = lastConnectionToken++;
|
connectionToken = lastConnectionToken++;
|
||||||
|
|
|
@ -69,6 +69,7 @@ private:
|
||||||
|
|
||||||
inline void encryptKeyWithSecret(uint8_t *array, uint8_t secretType);
|
inline void encryptKeyWithSecret(uint8_t *array, uint8_t secretType);
|
||||||
inline std::string *getCurrentSecret(uint8_t secretType);
|
inline std::string *getCurrentSecret(uint8_t secretType);
|
||||||
|
void onDisconnectedInternal(int32_t reason, int32_t error);
|
||||||
|
|
||||||
ProtocolType currentProtocolType = ProtocolTypeEE;
|
ProtocolType currentProtocolType = ProtocolTypeEE;
|
||||||
|
|
||||||
|
@ -94,6 +95,7 @@ private:
|
||||||
bool forceNextPort = false;
|
bool forceNextPort = false;
|
||||||
bool isMediaConnection = false;
|
bool isMediaConnection = false;
|
||||||
bool waitForReconnectTimer = false;
|
bool waitForReconnectTimer = false;
|
||||||
|
bool connectionInProcess = false;
|
||||||
uint32_t lastReconnectTimeout = 100;
|
uint32_t lastReconnectTimeout = 100;
|
||||||
int64_t usefullDataReceiveTime;
|
int64_t usefullDataReceiveTime;
|
||||||
uint32_t currentTimeout = 4;
|
uint32_t currentTimeout = 4;
|
||||||
|
|
|
@ -51,7 +51,8 @@ void ConnectionSocket::openConnection(std::string address, uint16_t port, bool i
|
||||||
isIpv6 = ipv6;
|
isIpv6 = ipv6;
|
||||||
currentAddress = address;
|
currentAddress = address;
|
||||||
currentPort = port;
|
currentPort = port;
|
||||||
int epolFd = ConnectionsManager::getInstance(instanceNum).epolFd;
|
waitingForHostResolve = "";
|
||||||
|
adjustWriteOpAfterResolve = false;
|
||||||
ConnectionsManager::getInstance(instanceNum).attachConnection(this);
|
ConnectionsManager::getInstance(instanceNum).attachConnection(this);
|
||||||
|
|
||||||
memset(&socketAddress, 0, sizeof(sockaddr_in));
|
memset(&socketAddress, 0, sizeof(sockaddr_in));
|
||||||
|
@ -96,32 +97,28 @@ void ConnectionSocket::openConnection(std::string address, uint16_t port, bool i
|
||||||
continueCheckAddress = false;
|
continueCheckAddress = false;
|
||||||
}
|
}
|
||||||
if (continueCheckAddress) {
|
if (continueCheckAddress) {
|
||||||
std::string host = ConnectionsManager::getInstance(instanceNum).delegate->getHostByName(*proxyAddress, instanceNum);
|
#ifdef USE_DELEGATE_HOST_RESOLVE
|
||||||
if (host.empty() || inet_pton(AF_INET, host.c_str(), &socketAddress.sin_addr.s_addr) != 1) {
|
waitingForHostResolve = *proxyAddress;
|
||||||
continueCheckAddress = true;
|
ConnectionsManager::getInstance(instanceNum).delegate->getHostByName(*proxyAddress, instanceNum, this);
|
||||||
if (LOGS_ENABLED) DEBUG_E("connection(%p) can't resolve host %s address via delegate", this, proxyAddress->c_str());
|
return;
|
||||||
|
#else
|
||||||
|
struct hostent *he;
|
||||||
|
if ((he = gethostbyname(proxyAddress->c_str())) == nullptr) {
|
||||||
|
if (LOGS_ENABLED) DEBUG_E("connection(%p) can't resolve host %s address", this, proxyAddress->c_str());
|
||||||
|
closeSocket(1, -1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
struct in_addr **addr_list = (struct in_addr **) he->h_addr_list;
|
||||||
|
if (addr_list[0] != nullptr) {
|
||||||
|
socketAddress.sin_addr.s_addr = addr_list[0]->s_addr;
|
||||||
|
if (LOGS_ENABLED) DEBUG_D("connection(%p) resolved host %s address %x", this, proxyAddress->c_str(), addr_list[0]->s_addr);
|
||||||
|
ipv6 = false;
|
||||||
} else {
|
} else {
|
||||||
continueCheckAddress = false;
|
if (LOGS_ENABLED) DEBUG_E("connection(%p) can't resolve host %s address", this, proxyAddress->c_str());
|
||||||
if (LOGS_ENABLED) DEBUG_D("connection(%p) resolved host %s address %x via delegate", this, proxyAddress->c_str(), socketAddress.sin_addr.s_addr);
|
closeSocket(1, -1);
|
||||||
}
|
return;
|
||||||
if (continueCheckAddress) {
|
|
||||||
struct hostent *he;
|
|
||||||
if ((he = gethostbyname(proxyAddress->c_str())) == nullptr) {
|
|
||||||
if (LOGS_ENABLED) DEBUG_E("connection(%p) can't resolve host %s address", this, proxyAddress->c_str());
|
|
||||||
closeSocket(1, -1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
struct in_addr **addr_list = (struct in_addr **) he->h_addr_list;
|
|
||||||
if (addr_list[0] != nullptr) {
|
|
||||||
socketAddress.sin_addr.s_addr = addr_list[0]->s_addr;
|
|
||||||
if (LOGS_ENABLED) DEBUG_D("connection(%p) resolved host %s address %x", this, proxyAddress->c_str(), addr_list[0]->s_addr);
|
|
||||||
ipv6 = false;
|
|
||||||
} else {
|
|
||||||
if (LOGS_ENABLED) DEBUG_E("connection(%p) can't resolve host %s address", this, proxyAddress->c_str());
|
|
||||||
closeSocket(1, -1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -150,6 +147,11 @@ void ConnectionSocket::openConnection(std::string address, uint16_t port, bool i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openConnectionInternal(ipv6);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionSocket::openConnectionInternal(bool ipv6) {
|
||||||
|
int epolFd = ConnectionsManager::getInstance(instanceNum).epolFd;
|
||||||
int yes = 1;
|
int yes = 1;
|
||||||
if (setsockopt(socketFd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(int))) {
|
if (setsockopt(socketFd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(int))) {
|
||||||
if (LOGS_ENABLED) DEBUG_E("connection(%p) set TCP_NODELAY failed", this);
|
if (LOGS_ENABLED) DEBUG_E("connection(%p) set TCP_NODELAY failed", this);
|
||||||
|
@ -171,6 +173,9 @@ void ConnectionSocket::openConnection(std::string address, uint16_t port, bool i
|
||||||
closeSocket(1, -1);
|
closeSocket(1, -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (adjustWriteOpAfterResolve) {
|
||||||
|
adjustWriteOp();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t ConnectionSocket::checkSocketError(int32_t *error) {
|
int32_t ConnectionSocket::checkSocketError(int32_t *error) {
|
||||||
|
@ -198,6 +203,8 @@ void ConnectionSocket::closeSocket(int32_t reason, int32_t error) {
|
||||||
}
|
}
|
||||||
socketFd = -1;
|
socketFd = -1;
|
||||||
}
|
}
|
||||||
|
waitingForHostResolve = "";
|
||||||
|
adjustWriteOpAfterResolve = false;
|
||||||
proxyAuthState = 0;
|
proxyAuthState = 0;
|
||||||
onConnectedSent = false;
|
onConnectedSent = false;
|
||||||
outgoingByteStream->clean();
|
outgoingByteStream->clean();
|
||||||
|
@ -401,6 +408,10 @@ void ConnectionSocket::writeBuffer(NativeByteBuffer *buffer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionSocket::adjustWriteOp() {
|
void ConnectionSocket::adjustWriteOp() {
|
||||||
|
if (!waitingForHostResolve.empty()) {
|
||||||
|
adjustWriteOpAfterResolve = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
eventMask.events = EPOLLIN | EPOLLRDHUP | EPOLLERR | EPOLLET;
|
eventMask.events = EPOLLIN | EPOLLRDHUP | EPOLLERR | EPOLLET;
|
||||||
if (proxyAuthState == 0 && (outgoingByteStream->hasData() || !onConnectedSent) || proxyAuthState == 1 || proxyAuthState == 3 || proxyAuthState == 5) {
|
if (proxyAuthState == 0 && (outgoingByteStream->hasData() || !onConnectedSent) || proxyAuthState == 1 || proxyAuthState == 3 || proxyAuthState == 5) {
|
||||||
eventMask.events |= EPOLLOUT;
|
eventMask.events |= EPOLLOUT;
|
||||||
|
@ -433,6 +444,10 @@ void ConnectionSocket::checkTimeout(int64_t now) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConnectionSocket::resetLastEventTime() {
|
||||||
|
lastEventTime = ConnectionsManager::getInstance(instanceNum).getCurrentTimeMonotonicMillis();
|
||||||
|
}
|
||||||
|
|
||||||
bool ConnectionSocket::isDisconnected() {
|
bool ConnectionSocket::isDisconnected() {
|
||||||
return socketFd < 0;
|
return socketFd < 0;
|
||||||
}
|
}
|
||||||
|
@ -447,4 +462,19 @@ void ConnectionSocket::setOverrideProxy(std::string address, uint16_t port, std:
|
||||||
overrideProxyUser = username;
|
overrideProxyUser = username;
|
||||||
overrideProxyPassword = password;
|
overrideProxyPassword = password;
|
||||||
overrideProxySecret = secret;
|
overrideProxySecret = secret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConnectionSocket::onHostNameResolved(std::string host, std::string ip, bool ipv6) {
|
||||||
|
ConnectionsManager::getInstance(instanceNum).scheduleTask([&, host, ip, ipv6] {
|
||||||
|
if (waitingForHostResolve == host) {
|
||||||
|
waitingForHostResolve = "";
|
||||||
|
if (ip.empty() || inet_pton(AF_INET, ip.c_str(), &socketAddress.sin_addr.s_addr) != 1) {
|
||||||
|
if (LOGS_ENABLED) DEBUG_E("connection(%p) can't resolve host %s address via delegate", this, host.c_str());
|
||||||
|
closeSocket(1, -1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (LOGS_ENABLED) DEBUG_D("connection(%p) resolved host %s address %x via delegate", this, ip.c_str(), socketAddress.sin_addr.s_addr);
|
||||||
|
openConnectionInternal(ipv6);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -32,11 +32,13 @@ public:
|
||||||
bool isDisconnected();
|
bool isDisconnected();
|
||||||
void dropConnection();
|
void dropConnection();
|
||||||
void setOverrideProxy(std::string address, uint16_t port, std::string username, std::string password, std::string secret);
|
void setOverrideProxy(std::string address, uint16_t port, std::string username, std::string password, std::string secret);
|
||||||
|
void onHostNameResolved(std::string host, std::string ip, bool ipv6);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int32_t instanceNum;
|
int32_t instanceNum;
|
||||||
void onEvent(uint32_t events);
|
void onEvent(uint32_t events);
|
||||||
void checkTimeout(int64_t now);
|
void checkTimeout(int64_t now);
|
||||||
|
void resetLastEventTime();
|
||||||
virtual void onReceivedData(NativeByteBuffer *buffer) = 0;
|
virtual void onReceivedData(NativeByteBuffer *buffer) = 0;
|
||||||
virtual void onDisconnected(int32_t reason, int32_t error) = 0;
|
virtual void onDisconnected(int32_t reason, int32_t error) = 0;
|
||||||
virtual void onConnected() = 0;
|
virtual void onConnected() = 0;
|
||||||
|
@ -63,12 +65,16 @@ private:
|
||||||
std::string currentAddress;
|
std::string currentAddress;
|
||||||
uint16_t currentPort;
|
uint16_t currentPort;
|
||||||
|
|
||||||
|
std::string waitingForHostResolve;
|
||||||
|
bool adjustWriteOpAfterResolve;
|
||||||
|
|
||||||
uint8_t buffer[1024];
|
uint8_t buffer[1024];
|
||||||
|
|
||||||
uint8_t proxyAuthState;
|
uint8_t proxyAuthState;
|
||||||
|
|
||||||
int32_t checkSocketError(int32_t *error);
|
int32_t checkSocketError(int32_t *error);
|
||||||
void closeSocket(int32_t reason, int32_t error);
|
void closeSocket(int32_t reason, int32_t error);
|
||||||
|
void openConnectionInternal(bool ipv6);
|
||||||
void adjustWriteOp();
|
void adjustWriteOp();
|
||||||
|
|
||||||
friend class EventObject;
|
friend class EventObject;
|
||||||
|
|
|
@ -159,7 +159,7 @@ int ConnectionsManager::callEvents(int64_t now) {
|
||||||
if (!networkPaused) {
|
if (!networkPaused) {
|
||||||
return 1000;
|
return 1000;
|
||||||
}
|
}
|
||||||
int32_t timeToPushPing = (int32_t) ((sendingPushPing ? 30000 : 60000 * 3) - llabs(now - lastPushPingTime));
|
int32_t timeToPushPing = (int32_t) ((sendingPushPing ? 30000 : nextPingTimeOffset) - llabs(now - lastPushPingTime));
|
||||||
if (timeToPushPing <= 0) {
|
if (timeToPushPing <= 0) {
|
||||||
return 1000;
|
return 1000;
|
||||||
}
|
}
|
||||||
|
@ -168,13 +168,19 @@ int ConnectionsManager::callEvents(int64_t now) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionsManager::checkPendingTasks() {
|
void ConnectionsManager::checkPendingTasks() {
|
||||||
|
int32_t count = INT_MAX;
|
||||||
while (true) {
|
while (true) {
|
||||||
std::function<void()> task;
|
std::function<void()> task;
|
||||||
pthread_mutex_lock(&mutex);
|
pthread_mutex_lock(&mutex);
|
||||||
if (pendingTasks.empty()) {
|
if (pendingTasks.empty() || count <= 0) {
|
||||||
pthread_mutex_unlock(&mutex);
|
pthread_mutex_unlock(&mutex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (count == INT_MAX) {
|
||||||
|
count = (int32_t) pendingTasks.size();
|
||||||
|
} else {
|
||||||
|
count--;
|
||||||
|
}
|
||||||
task = pendingTasks.front();
|
task = pendingTasks.front();
|
||||||
pendingTasks.pop();
|
pendingTasks.pop();
|
||||||
pthread_mutex_unlock(&mutex);
|
pthread_mutex_unlock(&mutex);
|
||||||
|
@ -199,7 +205,7 @@ void ConnectionsManager::select() {
|
||||||
|
|
||||||
Datacenter *datacenter = getDatacenterWithId(currentDatacenterId);
|
Datacenter *datacenter = getDatacenterWithId(currentDatacenterId);
|
||||||
if (pushConnectionEnabled) {
|
if (pushConnectionEnabled) {
|
||||||
if ((sendingPushPing && llabs(now - lastPushPingTime) >= 30000) || llabs(now - lastPushPingTime) >= 60000 * 3 + 10000) {
|
if ((sendingPushPing && llabs(now - lastPushPingTime) >= 30000) || llabs(now - lastPushPingTime) >= nextPingTimeOffset + 10000) {
|
||||||
lastPushPingTime = 0;
|
lastPushPingTime = 0;
|
||||||
sendingPushPing = false;
|
sendingPushPing = false;
|
||||||
if (datacenter != nullptr) {
|
if (datacenter != nullptr) {
|
||||||
|
@ -210,9 +216,12 @@ void ConnectionsManager::select() {
|
||||||
}
|
}
|
||||||
if (LOGS_ENABLED) DEBUG_D("push ping timeout");
|
if (LOGS_ENABLED) DEBUG_D("push ping timeout");
|
||||||
}
|
}
|
||||||
if (llabs(now - lastPushPingTime) >= 60000 * 3) {
|
if (llabs(now - lastPushPingTime) >= nextPingTimeOffset) {
|
||||||
if (LOGS_ENABLED) DEBUG_D("time for push ping");
|
if (LOGS_ENABLED) DEBUG_D("time for push ping");
|
||||||
lastPushPingTime = now;
|
lastPushPingTime = now;
|
||||||
|
uint8_t offset;
|
||||||
|
RAND_bytes(&offset, 1);
|
||||||
|
nextPingTimeOffset = 60000 * 3 + (offset % 40) - 20;
|
||||||
if (datacenter != nullptr) {
|
if (datacenter != nullptr) {
|
||||||
sendPing(datacenter, true);
|
sendPing(datacenter, true);
|
||||||
}
|
}
|
||||||
|
@ -669,35 +678,33 @@ void ConnectionsManager::onConnectionClosed(Connection *connection, int reason)
|
||||||
} else if (connection->getConnectionType() == ConnectionTypePush) {
|
} else if (connection->getConnectionType() == ConnectionTypePush) {
|
||||||
if (LOGS_ENABLED) DEBUG_D("connection(%p) push connection closed", connection);
|
if (LOGS_ENABLED) DEBUG_D("connection(%p) push connection closed", connection);
|
||||||
sendingPushPing = false;
|
sendingPushPing = false;
|
||||||
lastPushPingTime = getCurrentTimeMonotonicMillis() - 60000 * 3 + 4000;
|
lastPushPingTime = getCurrentTimeMonotonicMillis() - nextPingTimeOffset + 4000;
|
||||||
} else if (connection->getConnectionType() == ConnectionTypeProxy) {
|
} else if (connection->getConnectionType() == ConnectionTypeProxy) {
|
||||||
scheduleTask([&, connection] {
|
for (std::vector<std::unique_ptr<ProxyCheckInfo>>::iterator iter = proxyActiveChecks.begin(); iter != proxyActiveChecks.end(); iter++) {
|
||||||
for (std::vector<std::unique_ptr<ProxyCheckInfo>>::iterator iter = proxyActiveChecks.begin(); iter != proxyActiveChecks.end(); iter++) {
|
ProxyCheckInfo *proxyCheckInfo = iter->get();
|
||||||
ProxyCheckInfo *proxyCheckInfo = iter->get();
|
if (proxyCheckInfo->connectionNum == connection->getConnectionNum()) {
|
||||||
if (proxyCheckInfo->connectionNum == connection->getConnectionNum()) {
|
bool found = false;
|
||||||
bool found = false;
|
for (requestsIter iter2 = runningRequests.begin(); iter2 != runningRequests.end(); iter2++) {
|
||||||
for (requestsIter iter2 = runningRequests.begin(); iter2 != runningRequests.end(); iter2++) {
|
Request *request = iter2->get();
|
||||||
Request *request = iter2->get();
|
if (connection->getConnectionToken() == request->connectionToken && request->requestToken == proxyCheckInfo->requestToken && (request->connectionType & 0x0000ffff) == ConnectionTypeProxy) {
|
||||||
if (connection->getConnectionToken() == request->connectionToken && request->requestToken == proxyCheckInfo->requestToken && (request->connectionType & 0x0000ffff) == ConnectionTypeProxy) {
|
request->completed = true;
|
||||||
request->completed = true;
|
runningRequests.erase(iter2);
|
||||||
runningRequests.erase(iter2);
|
proxyCheckInfo->onRequestTime(-1);
|
||||||
proxyCheckInfo->onRequestTime(-1);
|
found = true;
|
||||||
found = true;
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (found) {
|
|
||||||
proxyActiveChecks.erase(iter);
|
|
||||||
if (!proxyCheckQueue.empty()) {
|
|
||||||
proxyCheckInfo = proxyCheckQueue[0].release();
|
|
||||||
proxyCheckQueue.erase(proxyCheckQueue.begin());
|
|
||||||
checkProxyInternal(proxyCheckInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
if (found) {
|
||||||
|
proxyActiveChecks.erase(iter);
|
||||||
|
if (!proxyCheckQueue.empty()) {
|
||||||
|
proxyCheckInfo = proxyCheckQueue[0].release();
|
||||||
|
proxyCheckQueue.erase(proxyCheckQueue.begin());
|
||||||
|
checkProxyInternal(proxyCheckInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2365,7 +2372,7 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t
|
||||||
} else {
|
} else {
|
||||||
currentCount = 0;
|
currentCount = 0;
|
||||||
}
|
}
|
||||||
if (!networkAvailable || currentCount >= 6) {
|
if (!networkAvailable || currentCount >= 10) {
|
||||||
iter++;
|
iter++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -2427,6 +2434,9 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t
|
||||||
networkMessage->invokeAfter = (request->requestFlags & RequestFlagInvokeAfter) != 0;
|
networkMessage->invokeAfter = (request->requestFlags & RequestFlagInvokeAfter) != 0;
|
||||||
networkMessage->needQuickAck = (request->requestFlags & RequestFlagNeedQuickAck) != 0;
|
networkMessage->needQuickAck = (request->requestFlags & RequestFlagNeedQuickAck) != 0;
|
||||||
|
|
||||||
|
if (!hasPendingRequestsForConnection(connection)) {
|
||||||
|
connection->resetLastEventTime();
|
||||||
|
}
|
||||||
runningRequests.push_back(std::move(*iter));
|
runningRequests.push_back(std::move(*iter));
|
||||||
|
|
||||||
switch (request->connectionType & 0x0000ffff) {
|
switch (request->connectionType & 0x0000ffff) {
|
||||||
|
@ -3202,7 +3212,7 @@ void ConnectionsManager::checkProxyInternal(ProxyCheckInfo *proxyCheckInfo) {
|
||||||
} else {
|
} else {
|
||||||
ConnectionType connectionType = (ConnectionType) (ConnectionTypeProxy | (freeConnectionNum << 16));
|
ConnectionType connectionType = (ConnectionType) (ConnectionTypeProxy | (freeConnectionNum << 16));
|
||||||
Datacenter *datacenter = getDatacenterWithId(DEFAULT_DATACENTER_ID);
|
Datacenter *datacenter = getDatacenterWithId(DEFAULT_DATACENTER_ID);
|
||||||
Connection *connection = datacenter->getConnectionByType(connectionType, true, 1);
|
Connection *connection = datacenter->getProxyConnection((uint8_t) freeConnectionNum, true, false);
|
||||||
if (connection != nullptr) {
|
if (connection != nullptr) {
|
||||||
connection->setOverrideProxy(proxyCheckInfo->address, proxyCheckInfo->port, proxyCheckInfo->username, proxyCheckInfo->password, proxyCheckInfo->secret);
|
connection->setOverrideProxy(proxyCheckInfo->address, proxyCheckInfo->port, proxyCheckInfo->username, proxyCheckInfo->password, proxyCheckInfo->secret);
|
||||||
connection->suspendConnection();
|
connection->suspendConnection();
|
||||||
|
|
|
@ -146,6 +146,7 @@ private:
|
||||||
int32_t currentPingTime = 0;
|
int32_t currentPingTime = 0;
|
||||||
bool registeringForPush = false;
|
bool registeringForPush = false;
|
||||||
int64_t lastPushPingTime = 0;
|
int64_t lastPushPingTime = 0;
|
||||||
|
int32_t nextPingTimeOffset = 60000 * 3;
|
||||||
bool sendingPushPing = false;
|
bool sendingPushPing = false;
|
||||||
bool sendingPing = false;
|
bool sendingPing = false;
|
||||||
bool updatingDcSettings = false;
|
bool updatingDcSettings = false;
|
||||||
|
@ -233,7 +234,6 @@ private:
|
||||||
friend class TL_message;
|
friend class TL_message;
|
||||||
friend class TL_rpc_result;
|
friend class TL_rpc_result;
|
||||||
friend class Config;
|
friend class Config;
|
||||||
friend class FileLoadOperation;
|
|
||||||
friend class FileLog;
|
friend class FileLog;
|
||||||
friend class Handshake;
|
friend class Handshake;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1255,13 +1255,16 @@ Connection *Datacenter::createConnectionByType(uint32_t connectionType) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connection *Datacenter::getProxyConnection(uint8_t num, bool create) {
|
Connection *Datacenter::getProxyConnection(uint8_t num, bool create, bool connect) {
|
||||||
ByteArray *authKey = getAuthKey(ConnectionTypeProxy, false, nullptr, 1);
|
ByteArray *authKey = getAuthKey(ConnectionTypeProxy, false, nullptr, 1);
|
||||||
if (authKey == nullptr) {
|
if (authKey == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (create) {
|
if (create) {
|
||||||
createProxyConnection(num)->connect();
|
Connection *connection = createProxyConnection(num);
|
||||||
|
if (connect) {
|
||||||
|
connection->connect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return proxyConnection[num];
|
return proxyConnection[num];
|
||||||
}
|
}
|
||||||
|
@ -1349,7 +1352,7 @@ Connection *Datacenter::getConnectionByType(uint32_t connectionType, bool create
|
||||||
case ConnectionTypeTemp:
|
case ConnectionTypeTemp:
|
||||||
return getTempConnection(create);
|
return getTempConnection(create);
|
||||||
case ConnectionTypeProxy:
|
case ConnectionTypeProxy:
|
||||||
return getProxyConnection(connectionNum, create);
|
return getProxyConnection(connectionNum, create, create);
|
||||||
default:
|
default:
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ public:
|
||||||
void resetInitVersion();
|
void resetInitVersion();
|
||||||
|
|
||||||
Connection *getDownloadConnection(uint8_t num, bool create);
|
Connection *getDownloadConnection(uint8_t num, bool create);
|
||||||
Connection *getProxyConnection(uint8_t num, bool create);
|
Connection *getProxyConnection(uint8_t num, bool create, bool connect);
|
||||||
Connection *getUploadConnection(uint8_t num, bool create);
|
Connection *getUploadConnection(uint8_t num, bool create);
|
||||||
Connection *getGenericConnection(bool create, int32_t allowPendingKey);
|
Connection *getGenericConnection(bool create, int32_t allowPendingKey);
|
||||||
Connection *getGenericMediaConnection(bool create, int32_t allowPendingKey);
|
Connection *getGenericMediaConnection(bool create, int32_t allowPendingKey);
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#define UPLOAD_CONNECTIONS_COUNT 4
|
#define UPLOAD_CONNECTIONS_COUNT 4
|
||||||
#define CONNECTION_BACKGROUND_KEEP_TIME 10000
|
#define CONNECTION_BACKGROUND_KEEP_TIME 10000
|
||||||
#define MAX_ACCOUNT_COUNT 3
|
#define MAX_ACCOUNT_COUNT 3
|
||||||
|
#define USE_DELEGATE_HOST_RESOLVE
|
||||||
|
|
||||||
#define DOWNLOAD_CHUNK_SIZE 1024 * 32
|
#define DOWNLOAD_CHUNK_SIZE 1024 * 32
|
||||||
#define DOWNLOAD_CHUNK_BIG_SIZE 1024 * 128
|
#define DOWNLOAD_CHUNK_BIG_SIZE 1024 * 128
|
||||||
|
@ -47,8 +48,8 @@ class Request;
|
||||||
class TL_message;
|
class TL_message;
|
||||||
class TL_config;
|
class TL_config;
|
||||||
class NativeByteBuffer;
|
class NativeByteBuffer;
|
||||||
class FileLoadOperation;
|
|
||||||
class Handshake;
|
class Handshake;
|
||||||
|
class ConnectionSocket;
|
||||||
|
|
||||||
typedef std::function<void(TLObject *response, TL_error *error, int32_t networkType)> onCompleteFunc;
|
typedef std::function<void(TLObject *response, TL_error *error, int32_t networkType)> onCompleteFunc;
|
||||||
typedef std::function<void()> onQuickAckFunc;
|
typedef std::function<void()> onQuickAckFunc;
|
||||||
|
@ -151,7 +152,7 @@ typedef struct ConnectiosManagerDelegate {
|
||||||
virtual void onBytesReceived(int32_t amount, int32_t networkType, int32_t instanceNum) = 0;
|
virtual void onBytesReceived(int32_t amount, int32_t networkType, int32_t instanceNum) = 0;
|
||||||
virtual void onRequestNewServerIpAndPort(int32_t second, int32_t instanceNum) = 0;
|
virtual void onRequestNewServerIpAndPort(int32_t second, int32_t instanceNum) = 0;
|
||||||
virtual void onProxyError(int32_t instanceNum) = 0;
|
virtual void onProxyError(int32_t instanceNum) = 0;
|
||||||
virtual std::string getHostByName(std::string domain, int32_t instanceNum) = 0;
|
virtual void getHostByName(std::string domain, int32_t instanceNum, ConnectionSocket *socket) = 0;
|
||||||
virtual int32_t getInitFlags(int32_t instanceNum) = 0;
|
virtual int32_t getInitFlags(int32_t instanceNum) = 0;
|
||||||
} ConnectiosManagerDelegate;
|
} ConnectiosManagerDelegate;
|
||||||
|
|
||||||
|
|
|
@ -1,395 +0,0 @@
|
||||||
/*
|
|
||||||
* This is the source code of tgnet library v. 1.1
|
|
||||||
* 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, 2015-2018.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "FileLoadOperation.h"
|
|
||||||
#include <algorithm>
|
|
||||||
#include "ApiScheme.h"
|
|
||||||
#include "ByteArray.h"
|
|
||||||
#include "MTProtoScheme.h"
|
|
||||||
#include "FileLog.h"
|
|
||||||
#include "ConnectionsManager.h"
|
|
||||||
#include "NativeByteBuffer.h"
|
|
||||||
#include "Datacenter.h"
|
|
||||||
|
|
||||||
FileLoadOperation::FileLoadOperation(int32_t dc_id, int64_t id, int64_t volume_id, int64_t access_hash, int32_t local_id, uint8_t *encKey, uint8_t *encIv, std::string extension, int32_t version, int32_t size, std::string dest, std::string temp) {
|
|
||||||
if (!dest.empty() && dest.find_last_of('/') != dest.size() - 1) {
|
|
||||||
dest += "/";
|
|
||||||
}
|
|
||||||
if (!temp.empty() && temp.find_last_of('/') != temp.size() - 1) {
|
|
||||||
temp += "/";
|
|
||||||
}
|
|
||||||
if (encKey != nullptr) {
|
|
||||||
location = std::unique_ptr<InputFileLocation>(new TL_inputEncryptedFileLocation());
|
|
||||||
location->id = id;
|
|
||||||
location->access_hash = access_hash;
|
|
||||||
location->volume_id = volume_id;
|
|
||||||
location->local_id = local_id;
|
|
||||||
key = std::unique_ptr<ByteArray>(new ByteArray(encKey, 32));
|
|
||||||
iv = std::unique_ptr<ByteArray>(new ByteArray(encIv, 32));
|
|
||||||
} else {
|
|
||||||
if (volume_id != 0) {
|
|
||||||
location = std::unique_ptr<InputFileLocation>(new TL_inputFileLocation());
|
|
||||||
location->volume_id = volume_id;
|
|
||||||
location->local_id = local_id;
|
|
||||||
location->secret = access_hash;
|
|
||||||
} else {
|
|
||||||
location = std::unique_ptr<InputFileLocation>(new TL_inputDocumentFileLocation());
|
|
||||||
location->id = id;
|
|
||||||
location->access_hash = access_hash;
|
|
||||||
location->version = version;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
destPath = dest;
|
|
||||||
tempPath = temp;
|
|
||||||
datacenter_id = dc_id;
|
|
||||||
totalBytesCount = size;
|
|
||||||
ext = extension;
|
|
||||||
if (key != nullptr) {
|
|
||||||
if (totalBytesCount % 16 != 0) {
|
|
||||||
bytesCountPadding = 16 - totalBytesCount % 16;
|
|
||||||
totalBytesCount += bytesCountPadding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FileLoadOperation::~FileLoadOperation() {
|
|
||||||
#ifdef ANDROID
|
|
||||||
if (ptr1 != nullptr) {
|
|
||||||
jniEnv[0]->DeleteGlobalRef(ptr1);
|
|
||||||
ptr1 = nullptr;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileLoadOperation::start() {
|
|
||||||
ConnectionsManager::getInstance(0).scheduleTask([&] {
|
|
||||||
if (state != FileLoadStateIdle) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
currentDownloadChunkSize = totalBytesCount >= DOWNLOAD_BIG_FILE_MIN_SIZE ? DOWNLOAD_CHUNK_BIG_SIZE : DOWNLOAD_CHUNK_SIZE;
|
|
||||||
currentMaxDownloadRequests = totalBytesCount >= DOWNLOAD_BIG_FILE_MIN_SIZE ? DOWNLOAD_MAX_BIG_REQUESTS : DOWNLOAD_MAX_REQUESTS;
|
|
||||||
state = FileLoadStateDownloading;
|
|
||||||
if (location == nullptr) {
|
|
||||||
onFailedLoadingFile(FileLoadFailReasonError);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::string prefix;
|
|
||||||
if (location->volume_id != 0 && location->local_id != 0) {
|
|
||||||
if (datacenter_id == INT_MIN || location->volume_id == INT_MIN || datacenter_id == 0) {
|
|
||||||
onFailedLoadingFile(FileLoadFailReasonError);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
prefix = to_string_uint64(location->volume_id) + "_" + to_string_int32(location->local_id);
|
|
||||||
} else {
|
|
||||||
if (datacenter_id == 0 || location->id == 0) {
|
|
||||||
onFailedLoadingFile(FileLoadFailReasonError);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
prefix = to_string_int32(datacenter_id) + "_" + to_string_uint64(location->id);
|
|
||||||
}
|
|
||||||
filePath = destPath + prefix + "." + ext;
|
|
||||||
tempFilePath = tempPath + prefix + ".temp";
|
|
||||||
if (key != nullptr) {
|
|
||||||
tempFileIvPath = tempPath + prefix + ".iv";
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE *destFile = fopen(filePath.c_str(), "rb");
|
|
||||||
if (destFile != nullptr) {
|
|
||||||
long len = ftell(destFile);
|
|
||||||
if (totalBytesCount != 0 && totalBytesCount != len) {
|
|
||||||
fclose(destFile);
|
|
||||||
destFile = nullptr;
|
|
||||||
remove(filePath.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (destFile == nullptr) {
|
|
||||||
tempFile = fopen(tempFilePath.c_str(), "r+b");
|
|
||||||
if (tempFile != nullptr) {
|
|
||||||
if (!fseek(tempFile, 0, SEEK_END) && (downloadedBytes = ftell(tempFile)) != -1L) {
|
|
||||||
nextDownloadOffset = downloadedBytes = downloadedBytes / currentDownloadChunkSize * currentDownloadChunkSize;
|
|
||||||
} else {
|
|
||||||
fclose(tempFile);
|
|
||||||
tempFile = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key != nullptr) {
|
|
||||||
if (tempFile != nullptr) {
|
|
||||||
tempIvFile = fopen(tempFileIvPath.c_str(), "r+b");
|
|
||||||
if (tempIvFile != nullptr) {
|
|
||||||
if (fread(iv->bytes, sizeof(uint8_t), 32, tempIvFile) != 32) {
|
|
||||||
fclose(tempIvFile);
|
|
||||||
tempIvFile = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tempIvFile == nullptr) {
|
|
||||||
tempIvFile = fopen(tempFileIvPath.c_str(), "w+b");
|
|
||||||
nextDownloadOffset = downloadedBytes = 0;
|
|
||||||
if (tempIvFile == nullptr) {
|
|
||||||
onFailedLoadingFile(FileLoadFailReasonError);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tempFile != nullptr) {
|
|
||||||
if (downloadedBytes != 0) {
|
|
||||||
if (!fseek(tempFile, downloadedBytes, SEEK_SET)) {
|
|
||||||
if (LOGS_ENABLED) DEBUG_D("resume loading file to temp = %s final = %s from %d", tempFilePath.c_str(), filePath.c_str(), nextDownloadOffset);
|
|
||||||
} else {
|
|
||||||
fclose(tempFile);
|
|
||||||
tempFile = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tempFile == nullptr) {
|
|
||||||
nextDownloadOffset = downloadedBytes = 0;
|
|
||||||
tempFile = fopen(tempFilePath.c_str(), "w+b");
|
|
||||||
if (tempFile == nullptr) {
|
|
||||||
onFailedLoadingFile(FileLoadFailReasonError);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (LOGS_ENABLED) DEBUG_D("start loading file to temp = %s final = %s", tempFilePath.c_str(), filePath.c_str());
|
|
||||||
}
|
|
||||||
if (totalBytesCount != 0 && downloadedBytes == totalBytesCount) {
|
|
||||||
onFinishLoadingFile();
|
|
||||||
} else {
|
|
||||||
startDownloadRequest();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fclose(destFile);
|
|
||||||
onFinishLoadingFile();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/*void setForceRequest(boolean forceRequest) { TODO
|
|
||||||
isForceRequest = forceRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isForceRequest() {
|
|
||||||
return isForceRequest;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
void FileLoadOperation::cancel() {
|
|
||||||
ConnectionsManager::getInstance(0).scheduleTask([&] {
|
|
||||||
if (state == FileLoadStateFinished || state == FileLoadStateFailed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onFailedLoadingFile(FileLoadFailReasonCanceled);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileLoadOperation::setDelegate(onFinishedFunc onFinished, onFailedFunc onFailed, onProgressChangedFunc onProgressChanged) {
|
|
||||||
onFinishedCallback = onFinished;
|
|
||||||
onFailedCallback = onFailed;
|
|
||||||
onProgressChangedCallback = onProgressChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileLoadOperation::cleanup() {
|
|
||||||
ConnectionsManager::getInstance(0).scheduleTask([&] {
|
|
||||||
if (tempFile != nullptr) {
|
|
||||||
fclose(tempFile);
|
|
||||||
tempFile = nullptr;
|
|
||||||
}
|
|
||||||
if (tempIvFile != nullptr) {
|
|
||||||
fclose(tempIvFile);
|
|
||||||
tempIvFile = nullptr;
|
|
||||||
}
|
|
||||||
for (size_t a = 0; a < requestInfos.size(); a++) {
|
|
||||||
if (requestInfos[a] != nullptr && requestInfos[a]->requestToken != 0) {
|
|
||||||
ConnectionsManager::getInstance(0).cancelRequestInternal(requestInfos[a]->requestToken, 0, true, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
requestInfos.clear();
|
|
||||||
delayedRequestInfos.clear();
|
|
||||||
delete this;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileLoadOperation::onFinishLoadingFile() {
|
|
||||||
if (state != FileLoadStateDownloading) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
state = FileLoadStateFinished;
|
|
||||||
if (tempIvFile != nullptr) {
|
|
||||||
fclose(tempIvFile);
|
|
||||||
tempIvFile = nullptr;
|
|
||||||
remove(tempFileIvPath.c_str());
|
|
||||||
}
|
|
||||||
if (tempFile != nullptr) {
|
|
||||||
fclose(tempFile);
|
|
||||||
tempFile = nullptr;
|
|
||||||
if (rename(tempFilePath.c_str(), filePath.c_str())) {
|
|
||||||
if (LOGS_ENABLED) DEBUG_E("unable to rename temp = %s to final = %s", tempFilePath.c_str(), filePath.c_str());
|
|
||||||
filePath = tempFilePath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (LOGS_ENABLED) DEBUG_D("finished downloading file %s", filePath.c_str());
|
|
||||||
if (onFinishedCallback != nullptr) {
|
|
||||||
onFinishedCallback(filePath);
|
|
||||||
}
|
|
||||||
cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileLoadOperation::onFailedLoadingFile(int reason) {
|
|
||||||
if (state == FileLoadStateFailed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
state = FileLoadStateFailed;
|
|
||||||
if (onFailedCallback != nullptr) {
|
|
||||||
onFailedCallback(FileLoadFailReasonCanceled);
|
|
||||||
}
|
|
||||||
cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileLoadOperation::processRequestResult(RequestInfo *requestInfo, TL_error *error, bool next) {
|
|
||||||
std::unique_ptr<RequestInfo> info;
|
|
||||||
if (!next) {
|
|
||||||
std::vector<std::unique_ptr<RequestInfo>>::iterator iter = std::find_if(requestInfos.begin(), requestInfos.end(), [&](std::unique_ptr<RequestInfo> &p) {
|
|
||||||
return p.get() == requestInfo;
|
|
||||||
});
|
|
||||||
if (iter != requestInfos.end()) {
|
|
||||||
info = std::move(*iter);
|
|
||||||
requestInfos.erase(iter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (error == nullptr) {
|
|
||||||
if (!next && downloadedBytes != requestInfo->offset) {
|
|
||||||
if (state == FileLoadStateDownloading) {
|
|
||||||
delayedRequestInfos.push_back(std::move(info));
|
|
||||||
startDownloadRequest();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (requestInfo->bytes == nullptr || requestInfo->bytes->limit() == 0) {
|
|
||||||
onFinishLoadingFile();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int32_t currentBytesSize = requestInfo->bytes->limit();
|
|
||||||
downloadedBytes += currentBytesSize;
|
|
||||||
bool finishedDownloading = currentBytesSize != currentDownloadChunkSize || ((totalBytesCount == downloadedBytes || downloadedBytes % currentDownloadChunkSize != 0) && (totalBytesCount <= 0 || totalBytesCount <= downloadedBytes));
|
|
||||||
|
|
||||||
if (key != nullptr) {
|
|
||||||
Datacenter::aesIgeEncryption(requestInfo->bytes->bytes(), key->bytes, iv->bytes, false, true, currentBytesSize);
|
|
||||||
if (finishedDownloading && bytesCountPadding != 0) {
|
|
||||||
requestInfo->bytes->limit(currentBytesSize = (currentBytesSize - bytesCountPadding));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tempFile != nullptr) {
|
|
||||||
if (fwrite(requestInfo->bytes->bytes(), sizeof(uint8_t), currentBytesSize, tempFile) != (uint32_t) currentBytesSize) {
|
|
||||||
onFailedLoadingFile(FileLoadFailReasonError);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tempIvFile != nullptr) {
|
|
||||||
if (fseek(tempIvFile, 0, SEEK_SET) || fwrite(iv->bytes, sizeof(uint8_t), 32, tempIvFile) != 32) {
|
|
||||||
onFailedLoadingFile(FileLoadFailReasonError);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (totalBytesCount > 0 && state == FileLoadStateDownloading) {
|
|
||||||
float progress = (float) downloadedBytes / (float) totalBytesCount;
|
|
||||||
if (progress > 1.0f) {
|
|
||||||
progress = 1.0f;
|
|
||||||
}
|
|
||||||
if (onProgressChangedCallback != nullptr) {
|
|
||||||
onProgressChangedCallback(progress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (std::vector<std::unique_ptr<RequestInfo>>::iterator iter = delayedRequestInfos.begin(); iter != delayedRequestInfos.end(); iter++) {
|
|
||||||
if (downloadedBytes == (*iter)->offset) {
|
|
||||||
info = std::move(*iter);
|
|
||||||
delayedRequestInfos.erase(iter);
|
|
||||||
processRequestResult(info.get(), nullptr, true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (finishedDownloading) {
|
|
||||||
onFinishLoadingFile();
|
|
||||||
} else {
|
|
||||||
startDownloadRequest();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
static std::string fileMigrate = "FILE_MIGRATE_";
|
|
||||||
static std::string offsetInvalid = "OFFSET_INVALID";
|
|
||||||
static std::string retryLimit = "RETRY_LIMIT";
|
|
||||||
if (error->text.find(fileMigrate) != std::string::npos) {
|
|
||||||
/*std::string num = error->text.substr(fileMigrate.size(), error->text.size() - fileMigrate.size());
|
|
||||||
int32_t dcId = atoi(num.c_str());
|
|
||||||
if (dcId <= 0) {
|
|
||||||
onFailedLoadingFile(FileLoadFailReasonError);
|
|
||||||
} else {
|
|
||||||
datacenter_id = dcId;
|
|
||||||
nextDownloadOffset = downloadedBytes = 0;
|
|
||||||
startDownloadRequest();
|
|
||||||
}*/
|
|
||||||
onFailedLoadingFile(FileLoadFailReasonError);
|
|
||||||
} else if (error->text.find(offsetInvalid) != std::string::npos) {
|
|
||||||
if (downloadedBytes % currentDownloadChunkSize == 0) {
|
|
||||||
onFinishLoadingFile();
|
|
||||||
} else {
|
|
||||||
onFailedLoadingFile(FileLoadFailReasonError);
|
|
||||||
}
|
|
||||||
} else if (error->text.find(retryLimit) != std::string::npos) {
|
|
||||||
onFailedLoadingFile(FileLoadFailReasonRetryLimit);
|
|
||||||
} else {
|
|
||||||
onFailedLoadingFile(FileLoadFailReasonError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileLoadOperation::startDownloadRequest() {
|
|
||||||
if (state != FileLoadStateDownloading || (totalBytesCount > 0 && nextDownloadOffset >= totalBytesCount) || ((requestInfos.size() + delayedRequestInfos.size()) >= currentMaxDownloadRequests)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int32_t count = 1;
|
|
||||||
if (totalBytesCount > 0) {
|
|
||||||
count = currentMaxDownloadRequests - requestInfos.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int32_t a = 0; a < count; a++) {
|
|
||||||
if (totalBytesCount > 0 && nextDownloadOffset >= totalBytesCount) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
bool isLast = totalBytesCount <= 0 || a == count - 1 || (totalBytesCount > 0 && (nextDownloadOffset + currentDownloadChunkSize) >= totalBytesCount);
|
|
||||||
|
|
||||||
RequestInfo *requestInfo = new RequestInfo();
|
|
||||||
requestInfos.push_back(std::unique_ptr<RequestInfo>(requestInfo));
|
|
||||||
|
|
||||||
TL_upload_getFile *request = new TL_upload_getFile();
|
|
||||||
request->location = location.get();
|
|
||||||
requestInfo->offset = request->offset = nextDownloadOffset;
|
|
||||||
request->limit = currentDownloadChunkSize;
|
|
||||||
nextDownloadOffset += currentDownloadChunkSize;
|
|
||||||
|
|
||||||
requestInfo->requestToken = ConnectionsManager::getInstance(0).sendRequest(request, [&, requestInfo](TLObject *response, TL_error *error, int32_t connectionType) {
|
|
||||||
requestInfo->requestToken = 0;
|
|
||||||
if (response != nullptr) {
|
|
||||||
TL_upload_file *res = (TL_upload_file *) response;
|
|
||||||
requestInfo->bytes = res->bytes;
|
|
||||||
res->bytes = nullptr;
|
|
||||||
}
|
|
||||||
processRequestResult(requestInfo, error, false);
|
|
||||||
}, nullptr, (isForceRequest ? RequestFlagForceDownload : 0) | RequestFlagFailOnServerErrors, datacenter_id, requestsCount % 2 == 0 ? ConnectionTypeDownload : (ConnectionType) (ConnectionTypeDownload | (1 << 16)), isLast);
|
|
||||||
requestsCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FileLoadOperation::RequestInfo::~RequestInfo() {
|
|
||||||
if (bytes != nullptr) {
|
|
||||||
bytes->reuse();
|
|
||||||
bytes = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
/*
|
|
||||||
* This is the source code of tgnet library v. 1.1
|
|
||||||
* 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, 2015-2018.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FILELOADOPERATION_H
|
|
||||||
#define FILELOADOPERATION_H
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include "Defines.h"
|
|
||||||
|
|
||||||
#ifdef ANDROID
|
|
||||||
#include <jni.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class TL_upload_file;
|
|
||||||
class InputFileLocation;
|
|
||||||
class ByteArray;
|
|
||||||
class FileLocation;
|
|
||||||
|
|
||||||
class FileLoadOperation {
|
|
||||||
|
|
||||||
public:
|
|
||||||
FileLoadOperation(int32_t dc_id, int64_t id, int64_t volume_id, int64_t access_hash, int32_t local_id, uint8_t *encKey, uint8_t *encIv, std::string extension, int32_t version, int32_t size, std::string dest, std::string temp);
|
|
||||||
~FileLoadOperation();
|
|
||||||
|
|
||||||
void start();
|
|
||||||
void cancel();
|
|
||||||
void setDelegate(onFinishedFunc onFinished, onFailedFunc onFailed, onProgressChangedFunc onProgressChanged);
|
|
||||||
|
|
||||||
#ifdef ANDROID
|
|
||||||
jobject ptr1 = nullptr;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
class RequestInfo {
|
|
||||||
|
|
||||||
public:
|
|
||||||
int32_t requestToken = 0;
|
|
||||||
int32_t offset = 0;
|
|
||||||
NativeByteBuffer *bytes = nullptr;
|
|
||||||
|
|
||||||
~RequestInfo();
|
|
||||||
};
|
|
||||||
|
|
||||||
void cleanup();
|
|
||||||
void onFinishLoadingFile();
|
|
||||||
void startDownloadRequest();
|
|
||||||
void processRequestResult(RequestInfo *requestInfo, TL_error *error, bool next);
|
|
||||||
void onFailedLoadingFile(int reason);
|
|
||||||
|
|
||||||
int32_t datacenter_id;
|
|
||||||
std::unique_ptr<InputFileLocation> location;
|
|
||||||
FileLoadState state = FileLoadStateIdle;
|
|
||||||
int32_t downloadedBytes = 0;
|
|
||||||
int32_t totalBytesCount = 0;
|
|
||||||
int32_t bytesCountPadding = 0;
|
|
||||||
std::unique_ptr<ByteArray> key;
|
|
||||||
std::unique_ptr<ByteArray> iv;
|
|
||||||
int32_t currentDownloadChunkSize = 0;
|
|
||||||
uint32_t currentMaxDownloadRequests = 0;
|
|
||||||
int32_t requestsCount = 0;
|
|
||||||
|
|
||||||
int32_t nextDownloadOffset = 0;
|
|
||||||
std::vector<std::unique_ptr<RequestInfo>> requestInfos;
|
|
||||||
std::vector<std::unique_ptr<RequestInfo>> delayedRequestInfos;
|
|
||||||
|
|
||||||
std::string ext;
|
|
||||||
|
|
||||||
std::string filePath;
|
|
||||||
std::string tempFilePath;
|
|
||||||
std::string tempFileIvPath;
|
|
||||||
|
|
||||||
FILE *tempFile = nullptr;
|
|
||||||
FILE *tempIvFile = nullptr;
|
|
||||||
|
|
||||||
std::string destPath;
|
|
||||||
std::string tempPath;
|
|
||||||
|
|
||||||
bool isForceRequest = false;
|
|
||||||
|
|
||||||
onFinishedFunc onFinishedCallback = nullptr;
|
|
||||||
onFailedFunc onFailedCallback = nullptr;
|
|
||||||
onProgressChangedFunc onProgressChangedCallback = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
3
TMessagesProj/proguard-rules.pro
vendored
|
@ -4,11 +4,8 @@
|
||||||
@com.google.android.gms.common.annotation.KeepName *;
|
@com.google.android.gms.common.annotation.KeepName *;
|
||||||
}
|
}
|
||||||
-keep class org.telegram.** { *; }
|
-keep class org.telegram.** { *; }
|
||||||
#-keep class com.google.android.exoplayer2.** { *; }
|
|
||||||
-keep class com.google.android.exoplayer2.ext.** { *; }
|
-keep class com.google.android.exoplayer2.ext.** { *; }
|
||||||
-keep class com.google.android.exoplayer2.util.** { *; }
|
-keep class com.google.android.exoplayer2.util.** { *; }
|
||||||
-keep class com.coremedia.** { *; }
|
|
||||||
-keep class com.googlecode.mp4parser.** { *; }
|
|
||||||
-dontwarn com.coremedia.**
|
-dontwarn com.coremedia.**
|
||||||
-dontwarn org.telegram.**
|
-dontwarn org.telegram.**
|
||||||
-dontwarn com.google.android.exoplayer2.ext.**
|
-dontwarn com.google.android.exoplayer2.ext.**
|
||||||
|
|
|
@ -70,8 +70,8 @@
|
||||||
android:name="org.telegram.messenger.${applicationClassName}"
|
android:name="org.telegram.messenger.${applicationClassName}"
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:hardwareAccelerated="@bool/useHardwareAcceleration"
|
android:hardwareAccelerated="@bool/useHardwareAcceleration"
|
||||||
android:icon="@drawable/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:roundIcon="@drawable/ic_launcher"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:largeHeap="true"
|
android:largeHeap="true"
|
||||||
android:theme="@style/Theme.TMessages.Start"
|
android:theme="@style/Theme.TMessages.Start"
|
||||||
android:manageSpaceActivity="org.telegram.ui.ExternalActionActivity"
|
android:manageSpaceActivity="org.telegram.ui.ExternalActionActivity"
|
||||||
|
@ -135,7 +135,7 @@
|
||||||
<data android:host="t.me" android:scheme="http" />
|
<data android:host="t.me" android:scheme="http" />
|
||||||
<data android:host="t.me" android:scheme="https" />
|
<data android:host="t.me" android:scheme="https" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter android:icon="@drawable/ic_launcher" android:priority="1">
|
<intent-filter android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:priority="1">
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
@ -151,7 +151,7 @@
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
android:stateNotNeeded="true"
|
android:stateNotNeeded="true"
|
||||||
android:theme="@style/Theme.TMessages.Transparent">
|
android:theme="@style/Theme.TMessages.Transparent">
|
||||||
<intent-filter android:icon="@drawable/ic_launcher" android:priority="1">
|
<intent-filter android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:priority="1">
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
@ -163,7 +163,7 @@
|
||||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||||
android:hardwareAccelerated="@bool/useHardwareAcceleration"
|
android:hardwareAccelerated="@bool/useHardwareAcceleration"
|
||||||
android:windowSoftInputMode="adjustPan">
|
android:windowSoftInputMode="adjustPan">
|
||||||
<intent-filter android:icon="@drawable/ic_launcher">
|
<intent-filter android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round">
|
||||||
<action android:name="org.telegram.passport.AUTHORIZE"/>
|
<action android:name="org.telegram.passport.AUTHORIZE"/>
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
@ -349,7 +349,7 @@
|
||||||
<receiver android:name=".voip.VoIPActionsReceiver" android:exported="false"/>
|
<receiver android:name=".voip.VoIPActionsReceiver" android:exported="false"/>
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="android.support.v4.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="${applicationId}.provider"
|
android:authorities="${applicationId}.provider"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:grantUriPermissions="true">
|
android:grantUriPermissions="true">
|
||||||
|
@ -369,6 +369,8 @@
|
||||||
android:name=".voip.CallNotificationSoundProvider"
|
android:name=".voip.CallNotificationSoundProvider"
|
||||||
android:exported="true"/>
|
android:exported="true"/>
|
||||||
|
|
||||||
|
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
|
||||||
|
|
||||||
<uses-library android:name="com.sec.android.app.multiwindow" android:required="false" />
|
<uses-library android:name="com.sec.android.app.multiwindow" android:required="false" />
|
||||||
<meta-data android:name="com.sec.android.support.multiwindow" android:value="true" />
|
<meta-data android:name="com.sec.android.support.multiwindow" android:value="true" />
|
||||||
<meta-data android:name="com.sec.android.multiwindow.DEFAULT_SIZE_W" android:value="632dp" />
|
<meta-data android:name="com.sec.android.multiwindow.DEFAULT_SIZE_W" android:value="632dp" />
|
||||||
|
@ -383,7 +385,6 @@
|
||||||
|
|
||||||
<meta-data android:name="com.google.android.gms.vision.DEPENDENCIES" android:value="face" />
|
<meta-data android:name="com.google.android.gms.vision.DEPENDENCIES" android:value="face" />
|
||||||
|
|
||||||
<meta-data android:name="com.samsung.android.icon_container.has_icon_container" android:value="true"/>
|
|
||||||
<meta-data android:name="android.max_aspect" android:value="2.5" />
|
<meta-data android:name="android.max_aspect" android:value="2.5" />
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
|
@ -30,8 +30,10 @@ emptyListPlaceholder=-11247768
|
||||||
chat_inAudioSelectedProgress=-14925469
|
chat_inAudioSelectedProgress=-14925469
|
||||||
chats_nameMessage=-8932123
|
chats_nameMessage=-8932123
|
||||||
chat_inMediaIcon=-1
|
chat_inMediaIcon=-1
|
||||||
|
actionBarDefaultArchived=-13684171
|
||||||
avatar_subtitleInProfileViolet=-7697782
|
avatar_subtitleInProfileViolet=-7697782
|
||||||
chat_messagePanelCancelInlineBot=-6971219
|
chat_messagePanelCancelInlineBot=-6971219
|
||||||
|
dialogSearchBackground=-13946829
|
||||||
chat_outAudioSeekbarFill=-8272902
|
chat_outAudioSeekbarFill=-8272902
|
||||||
chat_botKeyboardButtonBackgroundPressed=-13617349
|
chat_botKeyboardButtonBackgroundPressed=-13617349
|
||||||
player_time=-8550517
|
player_time=-8550517
|
||||||
|
@ -41,6 +43,7 @@ chat_inFileProgressSelected=-5845010
|
||||||
changephoneinfo_image=-10786960
|
changephoneinfo_image=-10786960
|
||||||
chat_inAudioPerfomerText=-8351328
|
chat_inAudioPerfomerText=-8351328
|
||||||
player_button=-1
|
player_button=-1
|
||||||
|
key_sheet_other=754974719
|
||||||
chat_inContactNameText=-1
|
chat_inContactNameText=-1
|
||||||
switch2Track=-2135965
|
switch2Track=-2135965
|
||||||
chats_menuPhoneCats=-8287602
|
chats_menuPhoneCats=-8287602
|
||||||
|
@ -53,12 +56,14 @@ chat_outVoiceSeekbar=-1300913456
|
||||||
player_actionBarTitle=-1
|
player_actionBarTitle=-1
|
||||||
dialogGrayLine=-13354948
|
dialogGrayLine=-13354948
|
||||||
chat_outFileIcon=-13143396
|
chat_outFileIcon=-13143396
|
||||||
|
chats_nameMessage_threeLines=-1644826
|
||||||
chat_inFileProgress=-10653824
|
chat_inFileProgress=-10653824
|
||||||
dialogIcon=-8747891
|
dialogIcon=-8747891
|
||||||
chat_emojiPanelEmptyText=-11511454
|
chat_emojiPanelEmptyText=-11511454
|
||||||
chat_emojiPanelBackspace=-10458511
|
chat_emojiPanelBackspace=-10458511
|
||||||
chat_inContactPhoneSelectedText=-8085320
|
chat_inContactPhoneSelectedText=-8085320
|
||||||
chat_replyPanelClose=-6905432
|
chat_replyPanelClose=-6905432
|
||||||
|
dialogSearchText=-723724
|
||||||
chat_outAudioTitleText=-1
|
chat_outAudioTitleText=-1
|
||||||
chat_emojiPanelBackground=-14999775
|
chat_emojiPanelBackground=-14999775
|
||||||
chats_unreadCounter=-11032854
|
chats_unreadCounter=-11032854
|
||||||
|
@ -97,6 +102,7 @@ chat_outFileSelectedIcon=-13925429
|
||||||
picker_disabledButton=-10524558
|
picker_disabledButton=-10524558
|
||||||
groupcreate_spanBackground=-14143949
|
groupcreate_spanBackground=-14143949
|
||||||
dialogButton=-10964761
|
dialogButton=-10964761
|
||||||
|
contextProgressInner1=800774134
|
||||||
chat_inLoaderPhotoIconSelected=-5648402
|
chat_inLoaderPhotoIconSelected=-5648402
|
||||||
actionBarDefaultSubtitle=-8156785
|
actionBarDefaultSubtitle=-8156785
|
||||||
chat_inContactPhoneText=-8812393
|
chat_inContactPhoneText=-8812393
|
||||||
|
@ -147,11 +153,13 @@ avatar_nameInMessageRed=-1084559
|
||||||
chat_outLoaderPhoto=-13077852
|
chat_outLoaderPhoto=-13077852
|
||||||
chat_botSwitchToInlineText=-9456666
|
chat_botSwitchToInlineText=-9456666
|
||||||
dialogTextRed2=-892058
|
dialogTextRed2=-892058
|
||||||
|
chats_nameMessageArchived=-9011322
|
||||||
avatar_nameInMessageOrange=-1265812
|
avatar_nameInMessageOrange=-1265812
|
||||||
chats_pinnedIcon=-10524305
|
chats_pinnedIcon=-10524305
|
||||||
chat_replyPanelLine=1907997
|
chat_replyPanelLine=1907997
|
||||||
avatar_subtitleInProfileOrange=-7697782
|
avatar_subtitleInProfileOrange=-7697782
|
||||||
chat_outSentCheckSelected=-7156228
|
chat_outSentCheckSelected=-7156228
|
||||||
|
dialogSearchHint=-8288371
|
||||||
chat_inVenueInfoSelectedText=-8085320
|
chat_inVenueInfoSelectedText=-8085320
|
||||||
dialogTextBlue2=-9456666
|
dialogTextBlue2=-9456666
|
||||||
avatar_backgroundGroupCreateSpanBlue=-12751207
|
avatar_backgroundGroupCreateSpanBlue=-12751207
|
||||||
|
@ -180,7 +188,7 @@ avatar_nameInMessageCyan=-8270884
|
||||||
chat_mediaLoaderPhotoIconSelected=-1842205
|
chat_mediaLoaderPhotoIconSelected=-1842205
|
||||||
chat_inLocationBackground=-13419450
|
chat_inLocationBackground=-13419450
|
||||||
radioBackground=-10524811
|
radioBackground=-10524811
|
||||||
contextProgressOuter1=-11358225
|
contextProgressOuter1=-10702088
|
||||||
chat_inFileIcon=-13946055
|
chat_inFileIcon=-13946055
|
||||||
avatar_backgroundActionBarPink=-14605274
|
avatar_backgroundActionBarPink=-14605274
|
||||||
dialogTextGray3=-7892583
|
dialogTextGray3=-7892583
|
||||||
|
@ -208,6 +216,7 @@ chat_outForwardedNameText=-5515009
|
||||||
dialogRoundCheckBox=-9912583
|
dialogRoundCheckBox=-9912583
|
||||||
chat_emojiPanelTrendingTitle=-2167820
|
chat_emojiPanelTrendingTitle=-2167820
|
||||||
switchThumbChecked=-13600600
|
switchThumbChecked=-13600600
|
||||||
|
chats_nameMessageArchived_threeLines=-1644826
|
||||||
chat_outSiteNameText=-5515009
|
chat_outSiteNameText=-5515009
|
||||||
windowBackgroundWhite=-14737118
|
windowBackgroundWhite=-14737118
|
||||||
chat_inVoiceSeekbarSelected=-2057139272
|
chat_inVoiceSeekbarSelected=-2057139272
|
||||||
|
@ -271,12 +280,12 @@ windowBackgroundWhiteGrayText=-9603715
|
||||||
musicPicker_buttonBackground=-11035162
|
musicPicker_buttonBackground=-11035162
|
||||||
avatar_actionBarSelectorViolet=-11972268
|
avatar_actionBarSelectorViolet=-11972268
|
||||||
avatar_nameInMessageBlue=-9456666
|
avatar_nameInMessageBlue=-9456666
|
||||||
dialogTextBlack=-1
|
dialogTextBlack=-328966
|
||||||
actionBarDefault=-14276309
|
actionBarDefault=-14276309
|
||||||
profile_actionIcon=-1
|
profile_actionIcon=-1
|
||||||
windowBackgroundUnchecked=-14473945
|
windowBackgroundUnchecked=-14473945
|
||||||
actionBarDefaultSelector=-11972268
|
actionBarDefaultSelector=-11972268
|
||||||
chats_menuTopShadow=-15724528
|
chats_menuTopShadow=-1558504677
|
||||||
chat_outAudioPerfomerText=-6965025
|
chat_outAudioPerfomerText=-6965025
|
||||||
sharedMedia_startStopLoadIcon=-11164432
|
sharedMedia_startStopLoadIcon=-11164432
|
||||||
chat_serviceBackgroundSelected=1614498132
|
chat_serviceBackgroundSelected=1614498132
|
||||||
|
@ -287,7 +296,7 @@ chat_outSentClockSelected=-6764038
|
||||||
switchTrackBlueSelectorChecked=848091135
|
switchTrackBlueSelectorChecked=848091135
|
||||||
musicPicker_checkbox=-11621658
|
musicPicker_checkbox=-11621658
|
||||||
chat_outFileBackground=-11829594
|
chat_outFileBackground=-11829594
|
||||||
chats_name=-1
|
chats_name=-657931
|
||||||
chat_attachSendBackground=-10242065
|
chat_attachSendBackground=-10242065
|
||||||
switchTrackBlueSelector=431611386
|
switchTrackBlueSelector=431611386
|
||||||
dialogBadgeBackground=-10371847
|
dialogBadgeBackground=-10371847
|
||||||
|
@ -338,6 +347,7 @@ chat_messagePanelVoiceDelete=-9997440
|
||||||
chat_inAudioProgress=-1
|
chat_inAudioProgress=-1
|
||||||
chats_date=-9011322
|
chats_date=-9011322
|
||||||
chat_messagePanelText=-1
|
chat_messagePanelText=-1
|
||||||
|
key_sheet_scrollUp=603979775
|
||||||
player_buttonActive=-9456666
|
player_buttonActive=-9456666
|
||||||
chat_outLoaderPhotoIcon=-9263664
|
chat_outLoaderPhotoIcon=-9263664
|
||||||
chat_outContactBackground=-9194520
|
chat_outContactBackground=-9194520
|
||||||
|
@ -360,10 +370,13 @@ chats_sentClock=-8740661
|
||||||
chat_inAudioSeekbar=-581869200
|
chat_inAudioSeekbar=-581869200
|
||||||
avatar_subtitleInProfileRed=-7697782
|
avatar_subtitleInProfileRed=-7697782
|
||||||
avatar_backgroundActionBarRed=-14605274
|
avatar_backgroundActionBarRed=-14605274
|
||||||
|
dialogSearchIcon=-8814973
|
||||||
chat_inPreviewInstantText=-9456666
|
chat_inPreviewInstantText=-9456666
|
||||||
|
chats_archiveBackground=-10642482
|
||||||
chat_inViews=-8812137
|
chat_inViews=-8812137
|
||||||
chat_outLoaderSelected=-9194520
|
chat_outLoaderSelected=-9194520
|
||||||
dialogButtonSelector=352321535
|
dialogButtonSelector=352321535
|
||||||
|
chats_archivePinBackground=-13749706
|
||||||
player_actionBarItems=-1
|
player_actionBarItems=-1
|
||||||
chat_sentError=-1551526
|
chat_sentError=-1551526
|
||||||
player_progressBackground=-13288897
|
player_progressBackground=-13288897
|
||||||
|
|
|
@ -22,7 +22,7 @@ switchTrack=-10984850
|
||||||
chat_inPreviewInstantSelectedText=-5648402
|
chat_inPreviewInstantSelectedText=-5648402
|
||||||
chat_attachAudioBackground=-619421
|
chat_attachAudioBackground=-619421
|
||||||
location_sendLocationBackground=-9919529
|
location_sendLocationBackground=-9919529
|
||||||
actionBarDefaultSubmenuBackground=-13878451
|
actionBarDefaultSubmenuBackground=-14075831
|
||||||
switchTrackBlueThumb=-14866637
|
switchTrackBlueThumb=-14866637
|
||||||
avatar_nameInMessageViolet=-6643205
|
avatar_nameInMessageViolet=-6643205
|
||||||
emptyListPlaceholder=-8549479
|
emptyListPlaceholder=-8549479
|
||||||
|
@ -30,8 +30,10 @@ chat_inAudioSelectedProgress=-1
|
||||||
chats_nameMessage=-1446156
|
chats_nameMessage=-1446156
|
||||||
chat_messagePanelShadow=2030043136
|
chat_messagePanelShadow=2030043136
|
||||||
chat_inMediaIcon=-1
|
chat_inMediaIcon=-1
|
||||||
|
actionBarDefaultArchived=-13748149
|
||||||
avatar_subtitleInProfileViolet=-7628894
|
avatar_subtitleInProfileViolet=-7628894
|
||||||
chat_messagePanelCancelInlineBot=-8549479
|
chat_messagePanelCancelInlineBot=-8549479
|
||||||
|
dialogSearchBackground=-14010037
|
||||||
chat_outAudioSeekbarFill=-7944965
|
chat_outAudioSeekbarFill=-7944965
|
||||||
chat_botKeyboardButtonBackgroundPressed=-12956574
|
chat_botKeyboardButtonBackgroundPressed=-12956574
|
||||||
player_time=-8549479
|
player_time=-8549479
|
||||||
|
@ -41,6 +43,7 @@ chat_inFileProgressSelected=-1
|
||||||
changephoneinfo_image=-12693922
|
changephoneinfo_image=-12693922
|
||||||
chat_inAudioPerfomerText=-8812393
|
chat_inAudioPerfomerText=-8812393
|
||||||
player_button=-1
|
player_button=-1
|
||||||
|
key_sheet_other=1140850687
|
||||||
chat_inContactNameText=-8796932
|
chat_inContactNameText=-8796932
|
||||||
chats_menuPhoneCats=-11049613
|
chats_menuPhoneCats=-11049613
|
||||||
chat_outPreviewLine=-6631937
|
chat_outPreviewLine=-6631937
|
||||||
|
@ -51,12 +54,14 @@ chat_outVoiceSeekbar=-429551165
|
||||||
player_actionBarTitle=-1
|
player_actionBarTitle=-1
|
||||||
dialogGrayLine=-15790062
|
dialogGrayLine=-15790062
|
||||||
chat_outFileIcon=-12689015
|
chat_outFileIcon=-12689015
|
||||||
|
chats_nameMessage_threeLines=-1446156
|
||||||
chat_inFileProgress=-1
|
chat_inFileProgress=-1
|
||||||
dialogIcon=-8944238
|
dialogIcon=-7627862
|
||||||
chat_emojiPanelEmptyText=-8549479
|
chat_emojiPanelEmptyText=-8549479
|
||||||
chat_emojiPanelBackspace=-9996665
|
chat_emojiPanelBackspace=-9996665
|
||||||
chat_replyPanelClose=-10062202
|
chat_replyPanelClose=-10062202
|
||||||
chat_inContactPhoneSelectedText=-7490861
|
chat_inContactPhoneSelectedText=-7490861
|
||||||
|
dialogSearchText=-1
|
||||||
chat_outAudioTitleText=-1
|
chat_outAudioTitleText=-1
|
||||||
chat_emojiPanelBackground=-14866637
|
chat_emojiPanelBackground=-14866637
|
||||||
chats_unreadCounter=-10177041
|
chats_unreadCounter=-10177041
|
||||||
|
@ -92,6 +97,7 @@ chat_outFileSelectedIcon=-13925429
|
||||||
picker_disabledButton=-11047552
|
picker_disabledButton=-11047552
|
||||||
groupcreate_spanBackground=-13878194
|
groupcreate_spanBackground=-13878194
|
||||||
dialogButton=-10177041
|
dialogButton=-10177041
|
||||||
|
contextProgressInner1=-11509903
|
||||||
chat_inLoaderPhotoIconSelected=-1
|
chat_inLoaderPhotoIconSelected=-1
|
||||||
actionBarDefaultSubtitle=-7628894
|
actionBarDefaultSubtitle=-7628894
|
||||||
chat_inContactPhoneText=-8812393
|
chat_inContactPhoneText=-8812393
|
||||||
|
@ -142,11 +148,13 @@ avatar_nameInMessageRed=-21124
|
||||||
chat_outLoaderPhoto=-12623479
|
chat_outLoaderPhoto=-12623479
|
||||||
chat_botSwitchToInlineText=-8796932
|
chat_botSwitchToInlineText=-8796932
|
||||||
dialogTextRed2=-1152913
|
dialogTextRed2=-1152913
|
||||||
|
chats_nameMessageArchived=-8549479
|
||||||
avatar_nameInMessageOrange=-13984
|
avatar_nameInMessageOrange=-13984
|
||||||
chats_pinnedIcon=-10982016
|
chats_pinnedIcon=-10982016
|
||||||
chat_replyPanelLine=1779898909
|
chat_replyPanelLine=1779898909
|
||||||
avatar_subtitleInProfileOrange=-7628894
|
avatar_subtitleInProfileOrange=-7628894
|
||||||
chat_outSentCheckSelected=-4268038
|
chat_outSentCheckSelected=-4268038
|
||||||
|
dialogSearchHint=-8419182
|
||||||
chat_inVenueInfoSelectedText=-7490861
|
chat_inVenueInfoSelectedText=-7490861
|
||||||
dialogTextBlue2=-10177041
|
dialogTextBlue2=-10177041
|
||||||
avatar_backgroundGroupCreateSpanBlue=-13803892
|
avatar_backgroundGroupCreateSpanBlue=-13803892
|
||||||
|
@ -173,7 +181,7 @@ windowBackgroundWhiteBlueText=-10177041
|
||||||
avatar_nameInMessageCyan=-10623523
|
avatar_nameInMessageCyan=-10623523
|
||||||
chat_inLocationBackground=-13417903
|
chat_inLocationBackground=-13417903
|
||||||
radioBackground=-1635939431
|
radioBackground=-1635939431
|
||||||
contextProgressOuter1=-10177041
|
contextProgressOuter1=-9914632
|
||||||
chat_inFileIcon=-14470078
|
chat_inFileIcon=-14470078
|
||||||
avatar_backgroundActionBarPink=-14602949
|
avatar_backgroundActionBarPink=-14602949
|
||||||
dialogTextGray3=-8549479
|
dialogTextGray3=-8549479
|
||||||
|
@ -195,22 +203,25 @@ chat_inBubbleSelected=-13546911
|
||||||
chat_mediaMenu=-1
|
chat_mediaMenu=-1
|
||||||
chat_outViewsSelected=-4268038
|
chat_outViewsSelected=-4268038
|
||||||
chat_outInstant=-7551233
|
chat_outInstant=-7551233
|
||||||
chat_emojiPanelShadowLine=251658239
|
chat_emojiPanelShadowLine=838860800
|
||||||
actionBarActionModeDefaultSelector=2047809827
|
actionBarActionModeDefaultSelector=2050907520
|
||||||
chat_outForwardedNameText=-7551233
|
chat_outForwardedNameText=-7551233
|
||||||
dialogRoundCheckBox=-10177041
|
dialogRoundCheckBox=-10177041
|
||||||
chat_emojiPanelTrendingTitle=-1
|
chat_emojiPanelTrendingTitle=-1
|
||||||
switchThumbChecked=-10376479
|
switchThumbChecked=-10376479
|
||||||
|
chat_stickersHintPanel=-13484721
|
||||||
chat_outSiteNameText=-1
|
chat_outSiteNameText=-1
|
||||||
windowBackgroundWhite=-14866637
|
windowBackgroundWhite=-14866637
|
||||||
groupcreate_offlineText=-8549479
|
groupcreate_offlineText=-8549479
|
||||||
chat_inVoiceSeekbarSelected=-9203285
|
chat_inVoiceSeekbarSelected=-9203285
|
||||||
dialogTextGray=-8549479
|
dialogTextGray=-8549479
|
||||||
chat_messageLinkOut=-6631937
|
chat_messageLinkOut=-6631937
|
||||||
|
avatar_backgroundArchived=-13087910
|
||||||
chat_outFileInfoSelectedText=-4268038
|
chat_outFileInfoSelectedText=-4268038
|
||||||
chats_tabletSelectedOverlay=268435455
|
chats_tabletSelectedOverlay=268435455
|
||||||
chat_outAudioDurationSelectedText=-4268038
|
chat_outAudioDurationSelectedText=-4268038
|
||||||
chat_attachCameraIcon1=-32171
|
chat_attachCameraIcon1=-32171
|
||||||
|
undo_background=-182112197
|
||||||
avatar_actionBarSelectorPink=-12758164
|
avatar_actionBarSelectorPink=-12758164
|
||||||
dialogTextHint=-8549479
|
dialogTextHint=-8549479
|
||||||
chat_topPanelTitle=-11164709
|
chat_topPanelTitle=-11164709
|
||||||
|
@ -226,7 +237,7 @@ chats_sentCheck=-10177041
|
||||||
chats_unreadCounterMuted=-12692893
|
chats_unreadCounterMuted=-12692893
|
||||||
chat_outVoiceSeekbarFill=-7944965
|
chat_outVoiceSeekbarFill=-7944965
|
||||||
chat_outReplyLine=-6631937
|
chat_outReplyLine=-6631937
|
||||||
chat_messagePanelIcons=-9996665
|
chat_messagePanelIcons=-9733492
|
||||||
chat_inReplyMediaMessageText=-8812393
|
chat_inReplyMediaMessageText=-8812393
|
||||||
inappPlayerTitle=-8549479
|
inappPlayerTitle=-8549479
|
||||||
chat_emojiPanelIconSelected=-10177041
|
chat_emojiPanelIconSelected=-10177041
|
||||||
|
@ -259,7 +270,7 @@ avatar_nameInMessagePink=-624741
|
||||||
windowBackgroundWhiteGrayText=-8549479
|
windowBackgroundWhiteGrayText=-8549479
|
||||||
avatar_actionBarSelectorViolet=-12758164
|
avatar_actionBarSelectorViolet=-12758164
|
||||||
avatar_nameInMessageBlue=-8796932
|
avatar_nameInMessageBlue=-8796932
|
||||||
dialogTextBlack=-1
|
dialogTextBlack=-592138
|
||||||
actionBarDefault=-14602949
|
actionBarDefault=-14602949
|
||||||
location_placeLocationBackground=-9919529
|
location_placeLocationBackground=-9919529
|
||||||
profile_actionIcon=-1
|
profile_actionIcon=-1
|
||||||
|
@ -330,6 +341,7 @@ chat_messagePanelVoiceDelete=-1
|
||||||
chat_inAudioProgress=-1
|
chat_inAudioProgress=-1
|
||||||
chats_date=-9207925
|
chats_date=-9207925
|
||||||
chat_messagePanelText=-1
|
chat_messagePanelText=-1
|
||||||
|
key_sheet_scrollUp=637534207
|
||||||
player_buttonActive=-10177041
|
player_buttonActive=-10177041
|
||||||
chat_outLoaderPhotoIcon=-1
|
chat_outLoaderPhotoIcon=-1
|
||||||
chat_outContactBackground=-9919529
|
chat_outContactBackground=-9919529
|
||||||
|
@ -345,17 +357,21 @@ windowBackgroundWhiteLinkSelection=862238205
|
||||||
player_background=-14734794
|
player_background=-14734794
|
||||||
inappPlayerClose=-8549479
|
inappPlayerClose=-8549479
|
||||||
chat_outMediaIcon=-1
|
chat_outMediaIcon=-1
|
||||||
|
chats_message_threeLines=-8549479
|
||||||
player_actionBarSubtitle=-8549479
|
player_actionBarSubtitle=-8549479
|
||||||
chat_outAudioCacheSeekbar=-10120765
|
chat_outAudioCacheSeekbar=-10120765
|
||||||
chats_sentClock=-11772054
|
chats_sentClock=-11772054
|
||||||
chat_inAudioSeekbar=-11443856
|
chat_inAudioSeekbar=-11443856
|
||||||
avatar_subtitleInProfileRed=-7628894
|
avatar_subtitleInProfileRed=-7628894
|
||||||
avatar_backgroundActionBarRed=-14602949
|
avatar_backgroundActionBarRed=-14602949
|
||||||
|
dialogSearchIcon=-8945521
|
||||||
chat_inPreviewInstantText=-8796932
|
chat_inPreviewInstantText=-8796932
|
||||||
|
chats_archiveBackground=-11036980
|
||||||
dialog_liveLocationProgress=-9919529
|
dialog_liveLocationProgress=-9919529
|
||||||
chat_inViews=-8812137
|
chat_inViews=-8812137
|
||||||
chat_outLoaderSelected=-9919529
|
chat_outLoaderSelected=-9919529
|
||||||
dialogButtonSelector=352321535
|
dialogButtonSelector=352321535
|
||||||
|
chats_archivePinBackground=-13746613
|
||||||
player_actionBarItems=-1
|
player_actionBarItems=-1
|
||||||
chat_sentError=-633010
|
chat_sentError=-633010
|
||||||
player_progressBackground=-13023400
|
player_progressBackground=-13023400
|
||||||
|
|
Before Width: | Height: | Size: 302 KiB |
Before Width: | Height: | Size: 353 KiB |
Before Width: | Height: | Size: 315 KiB |
Before Width: | Height: | Size: 316 KiB |
Before Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 136 KiB |
Before Width: | Height: | Size: 102 KiB |
Before Width: | Height: | Size: 76 KiB |
Before Width: | Height: | Size: 91 KiB |
Before Width: | Height: | Size: 145 KiB |
Before Width: | Height: | Size: 107 KiB |
Before Width: | Height: | Size: 96 KiB |
Before Width: | Height: | Size: 95 KiB |
Before Width: | Height: | Size: 100 KiB |
Before Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 145 KiB |
Before Width: | Height: | Size: 139 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_0_0.png
Normal file
After Width: | Height: | Size: 306 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_0_1.png
Normal file
After Width: | Height: | Size: 360 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_0_2.png
Normal file
After Width: | Height: | Size: 311 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_0_3.png
Normal file
After Width: | Height: | Size: 333 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_1_0.png
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_1_1.png
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_1_2.png
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_1_3.png
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_2_0.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_2_1.png
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_2_2.png
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_2_3.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_3_0.png
Normal file
After Width: | Height: | Size: 71 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_3_1.png
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_3_2.png
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_3_3.png
Normal file
After Width: | Height: | Size: 67 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_4_0.png
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_4_1.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_4_2.png
Normal file
After Width: | Height: | Size: 49 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_4_3.png
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_5_0.png
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_5_1.png
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_5_2.png
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_5_3.png
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_6_0.png
Normal file
After Width: | Height: | Size: 66 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_6_1.png
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_6_2.png
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_6_3.png
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_7_0.png
Normal file
After Width: | Height: | Size: 67 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_7_1.png
Normal file
After Width: | Height: | Size: 66 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_7_2.png
Normal file
After Width: | Height: | Size: 65 KiB |
BIN
TMessagesProj/src/main/assets/emoji/v14_emoji2.0x_7_3.png
Normal file
After Width: | Height: | Size: 68 KiB |
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2014 The Android Open Source Project
|
* Copyright 2018 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -14,11 +14,12 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.telegram.messenger.support.widget;
|
package androidx.recyclerview.widget;
|
||||||
|
|
||||||
import android.support.v4.util.Pools;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.core.util.Pools;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
|
@ -14,10 +14,9 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.telegram.messenger.support.util;
|
package androidx.recyclerview.widget;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import org.telegram.messenger.support.widget.RecyclerView;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ListUpdateCallback that dispatches update events to the given adapter.
|
* ListUpdateCallback that dispatches update events to the given adapter.
|
|
@ -0,0 +1,147 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package androidx.recyclerview.widget;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.RestrictTo;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration object for {@link ListAdapter}, {@link AsyncListDiffer}, and similar
|
||||||
|
* background-thread list diffing adapter logic.
|
||||||
|
* <p>
|
||||||
|
* At minimum, defines item diffing behavior with a {@link DiffUtil.ItemCallback}, used to compute
|
||||||
|
* item differences to pass to a RecyclerView adapter.
|
||||||
|
*
|
||||||
|
* @param <T> Type of items in the lists, and being compared.
|
||||||
|
*/
|
||||||
|
public final class AsyncDifferConfig<T> {
|
||||||
|
@Nullable
|
||||||
|
private final Executor mMainThreadExecutor;
|
||||||
|
@NonNull
|
||||||
|
private final Executor mBackgroundThreadExecutor;
|
||||||
|
@NonNull
|
||||||
|
private final DiffUtil.ItemCallback<T> mDiffCallback;
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||||
|
AsyncDifferConfig(
|
||||||
|
@Nullable Executor mainThreadExecutor,
|
||||||
|
@NonNull Executor backgroundThreadExecutor,
|
||||||
|
@NonNull DiffUtil.ItemCallback<T> diffCallback) {
|
||||||
|
mMainThreadExecutor = mainThreadExecutor;
|
||||||
|
mBackgroundThreadExecutor = backgroundThreadExecutor;
|
||||||
|
mDiffCallback = diffCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
|
||||||
|
@Nullable
|
||||||
|
public Executor getMainThreadExecutor() {
|
||||||
|
return mMainThreadExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
@NonNull
|
||||||
|
public Executor getBackgroundThreadExecutor() {
|
||||||
|
return mBackgroundThreadExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
@NonNull
|
||||||
|
public DiffUtil.ItemCallback<T> getDiffCallback() {
|
||||||
|
return mDiffCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder class for {@link AsyncDifferConfig}.
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
*/
|
||||||
|
public static final class Builder<T> {
|
||||||
|
@Nullable
|
||||||
|
private Executor mMainThreadExecutor;
|
||||||
|
private Executor mBackgroundThreadExecutor;
|
||||||
|
private final DiffUtil.ItemCallback<T> mDiffCallback;
|
||||||
|
|
||||||
|
public Builder(@NonNull DiffUtil.ItemCallback<T> diffCallback) {
|
||||||
|
mDiffCallback = diffCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If provided, defines the main thread executor used to dispatch adapter update
|
||||||
|
* notifications on the main thread.
|
||||||
|
* <p>
|
||||||
|
* If not provided, it will default to the main thread.
|
||||||
|
*
|
||||||
|
* @param executor The executor which can run tasks in the UI thread.
|
||||||
|
* @return this
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
|
||||||
|
@NonNull
|
||||||
|
public Builder<T> setMainThreadExecutor(Executor executor) {
|
||||||
|
mMainThreadExecutor = executor;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If provided, defines the background executor used to calculate the diff between an old
|
||||||
|
* and a new list.
|
||||||
|
* <p>
|
||||||
|
* If not provided, defaults to two thread pool executor, shared by all ListAdapterConfigs.
|
||||||
|
*
|
||||||
|
* @param executor The background executor to run list diffing.
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||||
|
@NonNull
|
||||||
|
public Builder<T> setBackgroundThreadExecutor(Executor executor) {
|
||||||
|
mBackgroundThreadExecutor = executor;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link AsyncListDiffer} with the given parameters.
|
||||||
|
*
|
||||||
|
* @return A new AsyncDifferConfig.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public AsyncDifferConfig<T> build() {
|
||||||
|
if (mBackgroundThreadExecutor == null) {
|
||||||
|
synchronized (sExecutorLock) {
|
||||||
|
if (sDiffExecutor == null) {
|
||||||
|
sDiffExecutor = Executors.newFixedThreadPool(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mBackgroundThreadExecutor = sDiffExecutor;
|
||||||
|
}
|
||||||
|
return new AsyncDifferConfig<>(
|
||||||
|
mMainThreadExecutor,
|
||||||
|
mBackgroundThreadExecutor,
|
||||||
|
mDiffCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove the below once supportlib has its own appropriate executors
|
||||||
|
private static final Object sExecutorLock = new Object();
|
||||||
|
private static Executor sDiffExecutor = null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,405 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package androidx.recyclerview.widget;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper for computing the difference between two lists via {@link DiffUtil} on a background
|
||||||
|
* thread.
|
||||||
|
* <p>
|
||||||
|
* It can be connected to a
|
||||||
|
* {@link RecyclerView.Adapter RecyclerView.Adapter}, and will signal the
|
||||||
|
* adapter of changes between sumbitted lists.
|
||||||
|
* <p>
|
||||||
|
* For simplicity, the {@link ListAdapter} wrapper class can often be used instead of the
|
||||||
|
* AsyncListDiffer directly. This AsyncListDiffer can be used for complex cases, where overriding an
|
||||||
|
* adapter base class to support asynchronous List diffing isn't convenient.
|
||||||
|
* <p>
|
||||||
|
* The AsyncListDiffer can consume the values from a LiveData of <code>List</code> and present the
|
||||||
|
* data simply for an adapter. It computes differences in list contents via {@link DiffUtil} on a
|
||||||
|
* background thread as new <code>List</code>s are received.
|
||||||
|
* <p>
|
||||||
|
* Use {@link #getCurrentList()} to access the current List, and present its data objects. Diff
|
||||||
|
* results will be dispatched to the ListUpdateCallback immediately before the current list is
|
||||||
|
* updated. If you're dispatching list updates directly to an Adapter, this means the Adapter can
|
||||||
|
* safely access list items and total size via {@link #getCurrentList()}.
|
||||||
|
* <p>
|
||||||
|
* A complete usage pattern with Room would look like this:
|
||||||
|
* <pre>
|
||||||
|
* {@literal @}Dao
|
||||||
|
* interface UserDao {
|
||||||
|
* {@literal @}Query("SELECT * FROM user ORDER BY lastName ASC")
|
||||||
|
* public abstract LiveData<List<User>> usersByLastName();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* class MyViewModel extends ViewModel {
|
||||||
|
* public final LiveData<List<User>> usersList;
|
||||||
|
* public MyViewModel(UserDao userDao) {
|
||||||
|
* usersList = userDao.usersByLastName();
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* class MyActivity extends AppCompatActivity {
|
||||||
|
* {@literal @}Override
|
||||||
|
* public void onCreate(Bundle savedState) {
|
||||||
|
* super.onCreate(savedState);
|
||||||
|
* MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
|
||||||
|
* RecyclerView recyclerView = findViewById(R.id.user_list);
|
||||||
|
* UserAdapter adapter = new UserAdapter();
|
||||||
|
* viewModel.usersList.observe(this, list -> adapter.submitList(list));
|
||||||
|
* recyclerView.setAdapter(adapter);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* class UserAdapter extends RecyclerView.Adapter<UserViewHolder> {
|
||||||
|
* private final AsyncListDiffer<User> mDiffer = new AsyncListDiffer(this, DIFF_CALLBACK);
|
||||||
|
* {@literal @}Override
|
||||||
|
* public int getItemCount() {
|
||||||
|
* return mDiffer.getCurrentList().size();
|
||||||
|
* }
|
||||||
|
* public void submitList(List<User> list) {
|
||||||
|
* mDiffer.submitList(list);
|
||||||
|
* }
|
||||||
|
* {@literal @}Override
|
||||||
|
* public void onBindViewHolder(UserViewHolder holder, int position) {
|
||||||
|
* User user = mDiffer.getCurrentList().get(position);
|
||||||
|
* holder.bindTo(user);
|
||||||
|
* }
|
||||||
|
* public static final DiffUtil.ItemCallback<User> DIFF_CALLBACK
|
||||||
|
* = new DiffUtil.ItemCallback<User>() {
|
||||||
|
* {@literal @}Override
|
||||||
|
* public boolean areItemsTheSame(
|
||||||
|
* {@literal @}NonNull User oldUser, {@literal @}NonNull User newUser) {
|
||||||
|
* // User properties may have changed if reloaded from the DB, but ID is fixed
|
||||||
|
* return oldUser.getId() == newUser.getId();
|
||||||
|
* }
|
||||||
|
* {@literal @}Override
|
||||||
|
* public boolean areContentsTheSame(
|
||||||
|
* {@literal @}NonNull User oldUser, {@literal @}NonNull User newUser) {
|
||||||
|
* // NOTE: if you use equals, your object must properly override Object#equals()
|
||||||
|
* // Incorrectly returning false here will result in too many animations.
|
||||||
|
* return oldUser.equals(newUser);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @param <T> Type of the lists this AsyncListDiffer will receive.
|
||||||
|
*
|
||||||
|
* @see DiffUtil
|
||||||
|
* @see AdapterListUpdateCallback
|
||||||
|
*/
|
||||||
|
public class AsyncListDiffer<T> {
|
||||||
|
private final ListUpdateCallback mUpdateCallback;
|
||||||
|
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||||
|
final AsyncDifferConfig<T> mConfig;
|
||||||
|
Executor mMainThreadExecutor;
|
||||||
|
|
||||||
|
private static class MainThreadExecutor implements Executor {
|
||||||
|
final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||||
|
MainThreadExecutor() {}
|
||||||
|
@Override
|
||||||
|
public void execute(@NonNull Runnable command) {
|
||||||
|
mHandler.post(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: use MainThreadExecutor from supportlib once one exists
|
||||||
|
private static final Executor sMainThreadExecutor = new MainThreadExecutor();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener for when the current List is updated.
|
||||||
|
*
|
||||||
|
* @param <T> Type of items in List
|
||||||
|
*/
|
||||||
|
public interface ListListener<T> {
|
||||||
|
/**
|
||||||
|
* Called after the current List has been updated.
|
||||||
|
*
|
||||||
|
* @param previousList The previous list.
|
||||||
|
* @param currentList The new current list.
|
||||||
|
*/
|
||||||
|
void onCurrentListChanged(@NonNull List<T> previousList, @NonNull List<T> currentList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final List<ListListener<T>> mListeners = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience for
|
||||||
|
* {@code AsyncListDiffer(new AdapterListUpdateCallback(adapter),
|
||||||
|
* new AsyncDifferConfig.Builder().setDiffCallback(diffCallback).build());}
|
||||||
|
*
|
||||||
|
* @param adapter Adapter to dispatch position updates to.
|
||||||
|
* @param diffCallback ItemCallback that compares items to dispatch appropriate animations when
|
||||||
|
*
|
||||||
|
* @see DiffUtil.DiffResult#dispatchUpdatesTo(RecyclerView.Adapter)
|
||||||
|
*/
|
||||||
|
public AsyncListDiffer(@NonNull RecyclerView.Adapter adapter,
|
||||||
|
@NonNull DiffUtil.ItemCallback<T> diffCallback) {
|
||||||
|
this(new AdapterListUpdateCallback(adapter),
|
||||||
|
new AsyncDifferConfig.Builder<>(diffCallback).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a AsyncListDiffer with the provided config, and ListUpdateCallback to dispatch
|
||||||
|
* updates to.
|
||||||
|
*
|
||||||
|
* @param listUpdateCallback Callback to dispatch updates to.
|
||||||
|
* @param config Config to define background work Executor, and DiffUtil.ItemCallback for
|
||||||
|
* computing List diffs.
|
||||||
|
*
|
||||||
|
* @see DiffUtil.DiffResult#dispatchUpdatesTo(RecyclerView.Adapter)
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
public AsyncListDiffer(@NonNull ListUpdateCallback listUpdateCallback,
|
||||||
|
@NonNull AsyncDifferConfig<T> config) {
|
||||||
|
mUpdateCallback = listUpdateCallback;
|
||||||
|
mConfig = config;
|
||||||
|
if (config.getMainThreadExecutor() != null) {
|
||||||
|
mMainThreadExecutor = config.getMainThreadExecutor();
|
||||||
|
} else {
|
||||||
|
mMainThreadExecutor = sMainThreadExecutor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private List<T> mList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Non-null, unmodifiable version of mList.
|
||||||
|
* <p>
|
||||||
|
* Collections.emptyList when mList is null, wrapped by Collections.unmodifiableList otherwise
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
private List<T> mReadOnlyList = Collections.emptyList();
|
||||||
|
|
||||||
|
// Max generation of currently scheduled runnable
|
||||||
|
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||||
|
int mMaxScheduledGeneration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current List - any diffing to present this list has already been computed and
|
||||||
|
* dispatched via the ListUpdateCallback.
|
||||||
|
* <p>
|
||||||
|
* If a <code>null</code> List, or no List has been submitted, an empty list will be returned.
|
||||||
|
* <p>
|
||||||
|
* The returned list may not be mutated - mutations to content must be done through
|
||||||
|
* {@link #submitList(List)}.
|
||||||
|
*
|
||||||
|
* @return current List.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public List<T> getCurrentList() {
|
||||||
|
return mReadOnlyList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pass a new List to the AdapterHelper. Adapter updates will be computed on a background
|
||||||
|
* thread.
|
||||||
|
* <p>
|
||||||
|
* If a List is already present, a diff will be computed asynchronously on a background thread.
|
||||||
|
* When the diff is computed, it will be applied (dispatched to the {@link ListUpdateCallback}),
|
||||||
|
* and the new List will be swapped in.
|
||||||
|
*
|
||||||
|
* @param newList The new List.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
public void submitList(@Nullable final List<T> newList) {
|
||||||
|
submitList(newList, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pass a new List to the AdapterHelper. Adapter updates will be computed on a background
|
||||||
|
* thread.
|
||||||
|
* <p>
|
||||||
|
* If a List is already present, a diff will be computed asynchronously on a background thread.
|
||||||
|
* When the diff is computed, it will be applied (dispatched to the {@link ListUpdateCallback}),
|
||||||
|
* and the new List will be swapped in.
|
||||||
|
* <p>
|
||||||
|
* The commit callback can be used to know when the List is committed, but note that it
|
||||||
|
* may not be executed. If List B is submitted immediately after List A, and is
|
||||||
|
* committed directly, the callback associated with List A will not be run.
|
||||||
|
*
|
||||||
|
* @param newList The new List.
|
||||||
|
* @param commitCallback Optional runnable that is executed when the List is committed, if
|
||||||
|
* it is committed.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
public void submitList(@Nullable final List<T> newList,
|
||||||
|
@Nullable final Runnable commitCallback) {
|
||||||
|
// incrementing generation means any currently-running diffs are discarded when they finish
|
||||||
|
final int runGeneration = ++mMaxScheduledGeneration;
|
||||||
|
|
||||||
|
if (newList == mList) {
|
||||||
|
// nothing to do (Note - still had to inc generation, since may have ongoing work)
|
||||||
|
if (commitCallback != null) {
|
||||||
|
commitCallback.run();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<T> previousList = mReadOnlyList;
|
||||||
|
|
||||||
|
// fast simple remove all
|
||||||
|
if (newList == null) {
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
int countRemoved = mList.size();
|
||||||
|
mList = null;
|
||||||
|
mReadOnlyList = Collections.emptyList();
|
||||||
|
// notify last, after list is updated
|
||||||
|
mUpdateCallback.onRemoved(0, countRemoved);
|
||||||
|
onCurrentListChanged(previousList, commitCallback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fast simple first insert
|
||||||
|
if (mList == null) {
|
||||||
|
mList = newList;
|
||||||
|
mReadOnlyList = Collections.unmodifiableList(newList);
|
||||||
|
// notify last, after list is updated
|
||||||
|
mUpdateCallback.onInserted(0, newList.size());
|
||||||
|
onCurrentListChanged(previousList, commitCallback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<T> oldList = mList;
|
||||||
|
mConfig.getBackgroundThreadExecutor().execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
final DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() {
|
||||||
|
@Override
|
||||||
|
public int getOldListSize() {
|
||||||
|
return oldList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getNewListSize() {
|
||||||
|
return newList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
|
||||||
|
T oldItem = oldList.get(oldItemPosition);
|
||||||
|
T newItem = newList.get(newItemPosition);
|
||||||
|
if (oldItem != null && newItem != null) {
|
||||||
|
return mConfig.getDiffCallback().areItemsTheSame(oldItem, newItem);
|
||||||
|
}
|
||||||
|
// If both items are null we consider them the same.
|
||||||
|
return oldItem == null && newItem == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
|
||||||
|
T oldItem = oldList.get(oldItemPosition);
|
||||||
|
T newItem = newList.get(newItemPosition);
|
||||||
|
if (oldItem != null && newItem != null) {
|
||||||
|
return mConfig.getDiffCallback().areContentsTheSame(oldItem, newItem);
|
||||||
|
}
|
||||||
|
if (oldItem == null && newItem == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// There is an implementation bug if we reach this point. Per the docs, this
|
||||||
|
// method should only be invoked when areItemsTheSame returns true. That
|
||||||
|
// only occurs when both items are non-null or both are null and both of
|
||||||
|
// those cases are handled above.
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
|
||||||
|
T oldItem = oldList.get(oldItemPosition);
|
||||||
|
T newItem = newList.get(newItemPosition);
|
||||||
|
if (oldItem != null && newItem != null) {
|
||||||
|
return mConfig.getDiffCallback().getChangePayload(oldItem, newItem);
|
||||||
|
}
|
||||||
|
// There is an implementation bug if we reach this point. Per the docs, this
|
||||||
|
// method should only be invoked when areItemsTheSame returns true AND
|
||||||
|
// areContentsTheSame returns false. That only occurs when both items are
|
||||||
|
// non-null which is the only case handled above.
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mMainThreadExecutor.execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (mMaxScheduledGeneration == runGeneration) {
|
||||||
|
latchList(newList, result, commitCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||||
|
void latchList(
|
||||||
|
@NonNull List<T> newList,
|
||||||
|
@NonNull DiffUtil.DiffResult diffResult,
|
||||||
|
@Nullable Runnable commitCallback) {
|
||||||
|
final List<T> previousList = mReadOnlyList;
|
||||||
|
mList = newList;
|
||||||
|
// notify last, after list is updated
|
||||||
|
mReadOnlyList = Collections.unmodifiableList(newList);
|
||||||
|
diffResult.dispatchUpdatesTo(mUpdateCallback);
|
||||||
|
onCurrentListChanged(previousList, commitCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onCurrentListChanged(@NonNull List<T> previousList,
|
||||||
|
@Nullable Runnable commitCallback) {
|
||||||
|
// current list is always mReadOnlyList
|
||||||
|
for (ListListener<T> listener : mListeners) {
|
||||||
|
listener.onCurrentListChanged(previousList, mReadOnlyList);
|
||||||
|
}
|
||||||
|
if (commitCallback != null) {
|
||||||
|
commitCallback.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a ListListener to receive updates when the current List changes.
|
||||||
|
*
|
||||||
|
* @param listener Listener to receive updates.
|
||||||
|
*
|
||||||
|
* @see #getCurrentList()
|
||||||
|
* @see #removeListListener(ListListener)
|
||||||
|
*/
|
||||||
|
public void addListListener(@NonNull ListListener<T> listener) {
|
||||||
|
mListeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a previously registered ListListener.
|
||||||
|
*
|
||||||
|
* @param listener Previously registered listener.
|
||||||
|
* @see #getCurrentList()
|
||||||
|
* @see #addListListener(ListListener)
|
||||||
|
*/
|
||||||
|
public void removeListListener(@NonNull ListListener<T> listener) {
|
||||||
|
mListeners.remove(listener);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 The Android Open Source Project
|
* Copyright 2018 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -14,14 +14,17 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.telegram.messenger.support.util;
|
package androidx.recyclerview.widget;
|
||||||
|
|
||||||
import android.support.annotation.UiThread;
|
|
||||||
import android.support.annotation.WorkerThread;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.SparseBooleanArray;
|
import android.util.SparseBooleanArray;
|
||||||
import android.util.SparseIntArray;
|
import android.util.SparseIntArray;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.UiThread;
|
||||||
|
import androidx.annotation.WorkerThread;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A utility class that supports asynchronous content loading.
|
* A utility class that supports asynchronous content loading.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -39,7 +42,7 @@ import android.util.SparseIntArray;
|
||||||
* Note that this class uses a single thread to load the data, so it suitable to load data from
|
* Note that this class uses a single thread to load the data, so it suitable to load data from
|
||||||
* secondary storage such as disk, but not from network.
|
* secondary storage such as disk, but not from network.
|
||||||
* <p>
|
* <p>
|
||||||
* This class is designed to work with {@link android.support.v7.widget.RecyclerView}, but it does
|
* This class is designed to work with {@link RecyclerView}, but it does
|
||||||
* not depend on it and can be used with other list views.
|
* not depend on it and can be used with other list views.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -84,8 +87,8 @@ public class AsyncListUtil<T> {
|
||||||
* @param dataCallback Data access callback.
|
* @param dataCallback Data access callback.
|
||||||
* @param viewCallback Callback for querying visible item range and update notifications.
|
* @param viewCallback Callback for querying visible item range and update notifications.
|
||||||
*/
|
*/
|
||||||
public AsyncListUtil(Class<T> klass, int tileSize, DataCallback<T> dataCallback,
|
public AsyncListUtil(@NonNull Class<T> klass, int tileSize,
|
||||||
ViewCallback viewCallback) {
|
@NonNull DataCallback<T> dataCallback, @NonNull ViewCallback viewCallback) {
|
||||||
mTClass = klass;
|
mTClass = klass;
|
||||||
mTileSize = tileSize;
|
mTileSize = tileSize;
|
||||||
mDataCallback = dataCallback;
|
mDataCallback = dataCallback;
|
||||||
|
@ -110,7 +113,7 @@ public class AsyncListUtil<T> {
|
||||||
* <p>
|
* <p>
|
||||||
* Identifies the data items that have not been loaded yet and initiates loading them in the
|
* Identifies the data items that have not been loaded yet and initiates loading them in the
|
||||||
* background. Should be called from the view's scroll listener (such as
|
* background. Should be called from the view's scroll listener (such as
|
||||||
* {@link android.support.v7.widget.RecyclerView.OnScrollListener#onScrolled}).
|
* {@link RecyclerView.OnScrollListener#onScrolled}).
|
||||||
*/
|
*/
|
||||||
public void onRangeChanged() {
|
public void onRangeChanged() {
|
||||||
if (isRefreshPending()) {
|
if (isRefreshPending()) {
|
||||||
|
@ -147,6 +150,7 @@ public class AsyncListUtil<T> {
|
||||||
* @return The data item at the given position or <code>null</code> if it has not been loaded
|
* @return The data item at the given position or <code>null</code> if it has not been loaded
|
||||||
* yet.
|
* yet.
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
public T getItem(int position) {
|
public T getItem(int position) {
|
||||||
if (position < 0 || position >= mItemCount) {
|
if (position < 0 || position >= mItemCount) {
|
||||||
throw new IndexOutOfBoundsException(position + " is not within 0 and " + mItemCount);
|
throw new IndexOutOfBoundsException(position + " is not within 0 and " + mItemCount);
|
||||||
|
@ -471,7 +475,7 @@ public class AsyncListUtil<T> {
|
||||||
* <code>itemCount</code>.
|
* <code>itemCount</code>.
|
||||||
*/
|
*/
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
public abstract void fillData(T[] data, int startPosition, int itemCount);
|
public abstract void fillData(@NonNull T[] data, int startPosition, int itemCount);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recycle the objects created in {@link #fillData} if necessary.
|
* Recycle the objects created in {@link #fillData} if necessary.
|
||||||
|
@ -481,7 +485,7 @@ public class AsyncListUtil<T> {
|
||||||
* @param itemCount The data item count.
|
* @param itemCount The data item count.
|
||||||
*/
|
*/
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
public void recycleData(T[] data, int itemCount) {
|
public void recycleData(@NonNull T[] data, int itemCount) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -548,7 +552,7 @@ public class AsyncListUtil<T> {
|
||||||
* @param outRange The visible item range.
|
* @param outRange The visible item range.
|
||||||
*/
|
*/
|
||||||
@UiThread
|
@UiThread
|
||||||
public abstract void getItemRangeInto(int[] outRange);
|
public abstract void getItemRangeInto(@NonNull int[] outRange);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute a wider range of items that will be loaded for smoother scrolling.
|
* Compute a wider range of items that will be loaded for smoother scrolling.
|
||||||
|
@ -569,7 +573,7 @@ public class AsyncListUtil<T> {
|
||||||
* @param scrollHint The scroll direction hint.
|
* @param scrollHint The scroll direction hint.
|
||||||
*/
|
*/
|
||||||
@UiThread
|
@UiThread
|
||||||
public void extendRangeInto(int[] range, int[] outRange, int scrollHint) {
|
public void extendRangeInto(@NonNull int[] range, @NonNull int[] outRange, int scrollHint) {
|
||||||
final int fullRange = range[1] - range[0] + 1;
|
final int fullRange = range[1] - range[0] + 1;
|
||||||
final int halfRange = fullRange / 2;
|
final int halfRange = fullRange / 2;
|
||||||
outRange[0] = range[0] - (scrollHint == HINT_SCROLL_DESC ? fullRange : halfRange);
|
outRange[0] = range[0] - (scrollHint == HINT_SCROLL_DESC ? fullRange : halfRange);
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
* Copyright 2018 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -13,7 +13,9 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.telegram.messenger.support.util;
|
package androidx.recyclerview.widget;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps a {@link ListUpdateCallback} callback and batches operations that can be merged.
|
* Wraps a {@link ListUpdateCallback} callback and batches operations that can be merged.
|
||||||
|
@ -22,7 +24,7 @@ package org.telegram.messenger.support.util;
|
||||||
* BatchingListUpdateCallback merges them and calls the wrapped callback only once.
|
* BatchingListUpdateCallback merges them and calls the wrapped callback only once.
|
||||||
* <p>
|
* <p>
|
||||||
* This is a general purpose class and is also used by
|
* This is a general purpose class and is also used by
|
||||||
* {@link android.support.v7.util.DiffUtil.DiffResult DiffResult} and
|
* {@link DiffUtil.DiffResult DiffResult} and
|
||||||
* {@link SortedList} to minimize the number of updates that are dispatched.
|
* {@link SortedList} to minimize the number of updates that are dispatched.
|
||||||
* <p>
|
* <p>
|
||||||
* If you use this class to batch updates, you must call {@link #dispatchLastEvent()} when the
|
* If you use this class to batch updates, you must call {@link #dispatchLastEvent()} when the
|
||||||
|
@ -41,7 +43,7 @@ public class BatchingListUpdateCallback implements ListUpdateCallback {
|
||||||
int mLastEventCount = -1;
|
int mLastEventCount = -1;
|
||||||
Object mLastEventPayload = null;
|
Object mLastEventPayload = null;
|
||||||
|
|
||||||
public BatchingListUpdateCallback(ListUpdateCallback callback) {
|
public BatchingListUpdateCallback(@NonNull ListUpdateCallback callback) {
|
||||||
mWrapped = callback;
|
mWrapped = callback;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2014 The Android Open Source Project
|
* Copyright 2018 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.telegram.messenger.support.widget;
|
package androidx.recyclerview.widget;
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2014 The Android Open Source Project
|
* Copyright 2018 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -13,18 +13,18 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.telegram.messenger.support.widget;
|
package androidx.recyclerview.widget;
|
||||||
|
|
||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.animation.AnimatorListenerAdapter;
|
import android.animation.AnimatorListenerAdapter;
|
||||||
import android.animation.TimeInterpolator;
|
import android.animation.TimeInterpolator;
|
||||||
import android.animation.ValueAnimator;
|
import android.animation.ValueAnimator;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v4.view.ViewCompat;
|
|
||||||
import org.telegram.messenger.support.widget.RecyclerView.ViewHolder;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewPropertyAnimator;
|
import android.view.ViewPropertyAnimator;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -40,27 +40,27 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
||||||
|
|
||||||
private static TimeInterpolator sDefaultInterpolator;
|
private static TimeInterpolator sDefaultInterpolator;
|
||||||
|
|
||||||
private ArrayList<ViewHolder> mPendingRemovals = new ArrayList<>();
|
private ArrayList<RecyclerView.ViewHolder> mPendingRemovals = new ArrayList<>();
|
||||||
private ArrayList<ViewHolder> mPendingAdditions = new ArrayList<>();
|
private ArrayList<RecyclerView.ViewHolder> mPendingAdditions = new ArrayList<>();
|
||||||
private ArrayList<MoveInfo> mPendingMoves = new ArrayList<>();
|
private ArrayList<MoveInfo> mPendingMoves = new ArrayList<>();
|
||||||
private ArrayList<ChangeInfo> mPendingChanges = new ArrayList<>();
|
private ArrayList<ChangeInfo> mPendingChanges = new ArrayList<>();
|
||||||
|
|
||||||
ArrayList<ArrayList<ViewHolder>> mAdditionsList = new ArrayList<>();
|
ArrayList<ArrayList<RecyclerView.ViewHolder>> mAdditionsList = new ArrayList<>();
|
||||||
ArrayList<ArrayList<MoveInfo>> mMovesList = new ArrayList<>();
|
ArrayList<ArrayList<MoveInfo>> mMovesList = new ArrayList<>();
|
||||||
ArrayList<ArrayList<ChangeInfo>> mChangesList = new ArrayList<>();
|
ArrayList<ArrayList<ChangeInfo>> mChangesList = new ArrayList<>();
|
||||||
|
|
||||||
ArrayList<ViewHolder> mAddAnimations = new ArrayList<>();
|
ArrayList<RecyclerView.ViewHolder> mAddAnimations = new ArrayList<>();
|
||||||
ArrayList<ViewHolder> mMoveAnimations = new ArrayList<>();
|
ArrayList<RecyclerView.ViewHolder> mMoveAnimations = new ArrayList<>();
|
||||||
ArrayList<ViewHolder> mRemoveAnimations = new ArrayList<>();
|
ArrayList<RecyclerView.ViewHolder> mRemoveAnimations = new ArrayList<>();
|
||||||
ArrayList<ViewHolder> mChangeAnimations = new ArrayList<>();
|
ArrayList<RecyclerView.ViewHolder> mChangeAnimations = new ArrayList<>();
|
||||||
|
|
||||||
private boolean delayAnimations = true;
|
private boolean delayAnimations = true;
|
||||||
|
|
||||||
private static class MoveInfo {
|
private static class MoveInfo {
|
||||||
public ViewHolder holder;
|
public RecyclerView.ViewHolder holder;
|
||||||
public int fromX, fromY, toX, toY;
|
public int fromX, fromY, toX, toY;
|
||||||
|
|
||||||
MoveInfo(ViewHolder holder, int fromX, int fromY, int toX, int toY) {
|
MoveInfo(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {
|
||||||
this.holder = holder;
|
this.holder = holder;
|
||||||
this.fromX = fromX;
|
this.fromX = fromX;
|
||||||
this.fromY = fromY;
|
this.fromY = fromY;
|
||||||
|
@ -70,14 +70,14 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ChangeInfo {
|
private static class ChangeInfo {
|
||||||
public ViewHolder oldHolder, newHolder;
|
public RecyclerView.ViewHolder oldHolder, newHolder;
|
||||||
public int fromX, fromY, toX, toY;
|
public int fromX, fromY, toX, toY;
|
||||||
private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder) {
|
private ChangeInfo(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder) {
|
||||||
this.oldHolder = oldHolder;
|
this.oldHolder = oldHolder;
|
||||||
this.newHolder = newHolder;
|
this.newHolder = newHolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder,
|
ChangeInfo(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder,
|
||||||
int fromX, int fromY, int toX, int toY) {
|
int fromX, int fromY, int toX, int toY) {
|
||||||
this(oldHolder, newHolder);
|
this(oldHolder, newHolder);
|
||||||
this.fromX = fromX;
|
this.fromX = fromX;
|
||||||
|
@ -110,13 +110,14 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// First, remove stuff
|
// First, remove stuff
|
||||||
for (ViewHolder holder : mPendingRemovals) {
|
for (RecyclerView.ViewHolder holder : mPendingRemovals) {
|
||||||
animateRemoveImpl(holder);
|
animateRemoveImpl(holder);
|
||||||
}
|
}
|
||||||
mPendingRemovals.clear();
|
mPendingRemovals.clear();
|
||||||
// Next, move stuff
|
// Next, move stuff
|
||||||
if (movesPending) {
|
if (movesPending) {
|
||||||
final ArrayList<MoveInfo> moves = new ArrayList<>(mPendingMoves);
|
final ArrayList<MoveInfo> moves = new ArrayList<>();
|
||||||
|
moves.addAll(mPendingMoves);
|
||||||
mMovesList.add(moves);
|
mMovesList.add(moves);
|
||||||
mPendingMoves.clear();
|
mPendingMoves.clear();
|
||||||
Runnable mover = new Runnable() {
|
Runnable mover = new Runnable() {
|
||||||
|
@ -139,7 +140,8 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
||||||
}
|
}
|
||||||
// Next, change stuff, to run in parallel with move animations
|
// Next, change stuff, to run in parallel with move animations
|
||||||
if (changesPending) {
|
if (changesPending) {
|
||||||
final ArrayList<ChangeInfo> changes = new ArrayList<>(mPendingChanges);
|
final ArrayList<ChangeInfo> changes = new ArrayList<>();
|
||||||
|
changes.addAll(mPendingChanges);
|
||||||
mChangesList.add(changes);
|
mChangesList.add(changes);
|
||||||
mPendingChanges.clear();
|
mPendingChanges.clear();
|
||||||
Runnable changer = new Runnable() {
|
Runnable changer = new Runnable() {
|
||||||
|
@ -153,7 +155,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (delayAnimations && removalsPending) {
|
if (delayAnimations && removalsPending) {
|
||||||
ViewHolder holder = changes.get(0).oldHolder;
|
RecyclerView.ViewHolder holder = changes.get(0).oldHolder;
|
||||||
ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());
|
ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());
|
||||||
} else {
|
} else {
|
||||||
changer.run();
|
changer.run();
|
||||||
|
@ -161,13 +163,14 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
||||||
}
|
}
|
||||||
// Next, add stuff
|
// Next, add stuff
|
||||||
if (additionsPending) {
|
if (additionsPending) {
|
||||||
final ArrayList<ViewHolder> additions = new ArrayList<>(mPendingAdditions);
|
final ArrayList<RecyclerView.ViewHolder> additions = new ArrayList<>();
|
||||||
|
additions.addAll(mPendingAdditions);
|
||||||
mAdditionsList.add(additions);
|
mAdditionsList.add(additions);
|
||||||
mPendingAdditions.clear();
|
mPendingAdditions.clear();
|
||||||
Runnable adder = new Runnable() {
|
Runnable adder = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
for (ViewHolder holder : additions) {
|
for (RecyclerView.ViewHolder holder : additions) {
|
||||||
animateAddImpl(holder);
|
animateAddImpl(holder);
|
||||||
}
|
}
|
||||||
additions.clear();
|
additions.clear();
|
||||||
|
@ -188,7 +191,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean animateRemove(final ViewHolder holder) {
|
public boolean animateRemove(final RecyclerView.ViewHolder holder) {
|
||||||
resetAnimation(holder);
|
resetAnimation(holder);
|
||||||
mPendingRemovals.add(holder);
|
mPendingRemovals.add(holder);
|
||||||
return true;
|
return true;
|
||||||
|
@ -198,7 +201,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
||||||
delayAnimations = value;
|
delayAnimations = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void animateRemoveImpl(final ViewHolder holder) {
|
private void animateRemoveImpl(final RecyclerView.ViewHolder holder) {
|
||||||
final View view = holder.itemView;
|
final View view = holder.itemView;
|
||||||
final ViewPropertyAnimator animation = view.animate();
|
final ViewPropertyAnimator animation = view.animate();
|
||||||
mRemoveAnimations.add(holder);
|
mRemoveAnimations.add(holder);
|
||||||
|
@ -221,14 +224,14 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean animateAdd(final ViewHolder holder) {
|
public boolean animateAdd(final RecyclerView.ViewHolder holder) {
|
||||||
resetAnimation(holder);
|
resetAnimation(holder);
|
||||||
holder.itemView.setAlpha(0);
|
holder.itemView.setAlpha(0);
|
||||||
mPendingAdditions.add(holder);
|
mPendingAdditions.add(holder);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void animateAddImpl(final ViewHolder holder) {
|
void animateAddImpl(final RecyclerView.ViewHolder holder) {
|
||||||
final View view = holder.itemView;
|
final View view = holder.itemView;
|
||||||
final ViewPropertyAnimator animation = view.animate();
|
final ViewPropertyAnimator animation = view.animate();
|
||||||
mAddAnimations.add(holder);
|
mAddAnimations.add(holder);
|
||||||
|
@ -255,7 +258,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean animateMove(final ViewHolder holder, int fromX, int fromY,
|
public boolean animateMove(final RecyclerView.ViewHolder holder, int fromX, int fromY,
|
||||||
int toX, int toY) {
|
int toX, int toY) {
|
||||||
final View view = holder.itemView;
|
final View view = holder.itemView;
|
||||||
fromX += (int) holder.itemView.getTranslationX();
|
fromX += (int) holder.itemView.getTranslationX();
|
||||||
|
@ -277,7 +280,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) {
|
void animateMoveImpl(final RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {
|
||||||
final View view = holder.itemView;
|
final View view = holder.itemView;
|
||||||
final int deltaX = toX - fromX;
|
final int deltaX = toX - fromX;
|
||||||
final int deltaY = toY - fromY;
|
final int deltaY = toY - fromY;
|
||||||
|
@ -319,7 +322,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder,
|
public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder,
|
||||||
int fromX, int fromY, int toX, int toY) {
|
int fromX, int fromY, int toX, int toY) {
|
||||||
if (oldHolder == newHolder) {
|
if (oldHolder == newHolder) {
|
||||||
// Don't know how to run change animations when the same view holder is re-used.
|
// Don't know how to run change animations when the same view holder is re-used.
|
||||||
|
@ -348,9 +351,9 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
||||||
}
|
}
|
||||||
|
|
||||||
void animateChangeImpl(final ChangeInfo changeInfo) {
|
void animateChangeImpl(final ChangeInfo changeInfo) {
|
||||||
final ViewHolder holder = changeInfo.oldHolder;
|
final RecyclerView.ViewHolder holder = changeInfo.oldHolder;
|
||||||
final View view = holder == null ? null : holder.itemView;
|
final View view = holder == null ? null : holder.itemView;
|
||||||
final ViewHolder newHolder = changeInfo.newHolder;
|
final RecyclerView.ViewHolder newHolder = changeInfo.newHolder;
|
||||||
final View newView = newHolder != null ? newHolder.itemView : null;
|
final View newView = newHolder != null ? newHolder.itemView : null;
|
||||||
if (view != null) {
|
if (view != null) {
|
||||||
final ViewPropertyAnimator oldViewAnim = view.animate().setDuration(
|
final ViewPropertyAnimator oldViewAnim = view.animate().setDuration(
|
||||||
|
@ -399,7 +402,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void endChangeAnimation(List<ChangeInfo> infoList, ViewHolder item) {
|
private void endChangeAnimation(List<ChangeInfo> infoList, RecyclerView.ViewHolder item) {
|
||||||
for (int i = infoList.size() - 1; i >= 0; i--) {
|
for (int i = infoList.size() - 1; i >= 0; i--) {
|
||||||
ChangeInfo changeInfo = infoList.get(i);
|
ChangeInfo changeInfo = infoList.get(i);
|
||||||
if (endChangeAnimationIfNecessary(changeInfo, item)) {
|
if (endChangeAnimationIfNecessary(changeInfo, item)) {
|
||||||
|
@ -418,7 +421,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
||||||
endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder);
|
endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, ViewHolder item) {
|
private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, RecyclerView.ViewHolder item) {
|
||||||
boolean oldItem = false;
|
boolean oldItem = false;
|
||||||
if (changeInfo.newHolder == item) {
|
if (changeInfo.newHolder == item) {
|
||||||
changeInfo.newHolder = null;
|
changeInfo.newHolder = null;
|
||||||
|
@ -436,7 +439,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void endAnimation(ViewHolder item) {
|
public void endAnimation(RecyclerView.ViewHolder item) {
|
||||||
final View view = item.itemView;
|
final View view = item.itemView;
|
||||||
// this will trigger end callback which should set properties to their target values.
|
// this will trigger end callback which should set properties to their target values.
|
||||||
view.animate().cancel();
|
view.animate().cancel();
|
||||||
|
@ -484,7 +487,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int i = mAdditionsList.size() - 1; i >= 0; i--) {
|
for (int i = mAdditionsList.size() - 1; i >= 0; i--) {
|
||||||
ArrayList<ViewHolder> additions = mAdditionsList.get(i);
|
ArrayList<RecyclerView.ViewHolder> additions = mAdditionsList.get(i);
|
||||||
if (additions.remove(item)) {
|
if (additions.remove(item)) {
|
||||||
view.setAlpha(1);
|
view.setAlpha(1);
|
||||||
dispatchAddFinished(item);
|
dispatchAddFinished(item);
|
||||||
|
@ -521,7 +524,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
||||||
dispatchFinishedWhenDone();
|
dispatchFinishedWhenDone();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetAnimation(ViewHolder holder) {
|
private void resetAnimation(RecyclerView.ViewHolder holder) {
|
||||||
if (sDefaultInterpolator == null) {
|
if (sDefaultInterpolator == null) {
|
||||||
sDefaultInterpolator = new ValueAnimator().getInterpolator();
|
sDefaultInterpolator = new ValueAnimator().getInterpolator();
|
||||||
}
|
}
|
||||||
|
@ -552,9 +555,14 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
||||||
void dispatchFinishedWhenDone() {
|
void dispatchFinishedWhenDone() {
|
||||||
if (!isRunning()) {
|
if (!isRunning()) {
|
||||||
dispatchAnimationsFinished();
|
dispatchAnimationsFinished();
|
||||||
|
onAllAnimationsDone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void onAllAnimationsDone() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void endAnimations() {
|
public void endAnimations() {
|
||||||
int count = mPendingMoves.size();
|
int count = mPendingMoves.size();
|
||||||
|
@ -568,13 +576,13 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
||||||
}
|
}
|
||||||
count = mPendingRemovals.size();
|
count = mPendingRemovals.size();
|
||||||
for (int i = count - 1; i >= 0; i--) {
|
for (int i = count - 1; i >= 0; i--) {
|
||||||
ViewHolder item = mPendingRemovals.get(i);
|
RecyclerView.ViewHolder item = mPendingRemovals.get(i);
|
||||||
dispatchRemoveFinished(item);
|
dispatchRemoveFinished(item);
|
||||||
mPendingRemovals.remove(i);
|
mPendingRemovals.remove(i);
|
||||||
}
|
}
|
||||||
count = mPendingAdditions.size();
|
count = mPendingAdditions.size();
|
||||||
for (int i = count - 1; i >= 0; i--) {
|
for (int i = count - 1; i >= 0; i--) {
|
||||||
ViewHolder item = mPendingAdditions.get(i);
|
RecyclerView.ViewHolder item = mPendingAdditions.get(i);
|
||||||
item.itemView.setAlpha(1);
|
item.itemView.setAlpha(1);
|
||||||
dispatchAddFinished(item);
|
dispatchAddFinished(item);
|
||||||
mPendingAdditions.remove(i);
|
mPendingAdditions.remove(i);
|
||||||
|
@ -594,7 +602,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
||||||
count = moves.size();
|
count = moves.size();
|
||||||
for (int j = count - 1; j >= 0; j--) {
|
for (int j = count - 1; j >= 0; j--) {
|
||||||
MoveInfo moveInfo = moves.get(j);
|
MoveInfo moveInfo = moves.get(j);
|
||||||
ViewHolder item = moveInfo.holder;
|
RecyclerView.ViewHolder item = moveInfo.holder;
|
||||||
View view = item.itemView;
|
View view = item.itemView;
|
||||||
view.setTranslationY(0);
|
view.setTranslationY(0);
|
||||||
view.setTranslationX(0);
|
view.setTranslationX(0);
|
||||||
|
@ -607,10 +615,10 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
||||||
}
|
}
|
||||||
listCount = mAdditionsList.size();
|
listCount = mAdditionsList.size();
|
||||||
for (int i = listCount - 1; i >= 0; i--) {
|
for (int i = listCount - 1; i >= 0; i--) {
|
||||||
ArrayList<ViewHolder> additions = mAdditionsList.get(i);
|
ArrayList<RecyclerView.ViewHolder> additions = mAdditionsList.get(i);
|
||||||
count = additions.size();
|
count = additions.size();
|
||||||
for (int j = count - 1; j >= 0; j--) {
|
for (int j = count - 1; j >= 0; j--) {
|
||||||
ViewHolder item = additions.get(j);
|
RecyclerView.ViewHolder item = additions.get(j);
|
||||||
View view = item.itemView;
|
View view = item.itemView;
|
||||||
view.setAlpha(1);
|
view.setAlpha(1);
|
||||||
dispatchAddFinished(item);
|
dispatchAddFinished(item);
|
||||||
|
@ -640,7 +648,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
||||||
dispatchAnimationsFinished();
|
dispatchAnimationsFinished();
|
||||||
}
|
}
|
||||||
|
|
||||||
void cancelAll(List<ViewHolder> viewHolders) {
|
void cancelAll(List<RecyclerView.ViewHolder> viewHolders) {
|
||||||
for (int i = viewHolders.size() - 1; i >= 0; i--) {
|
for (int i = viewHolders.size() - 1; i >= 0; i--) {
|
||||||
viewHolders.get(i).itemView.animate().cancel();
|
viewHolders.get(i).itemView.animate().cancel();
|
||||||
}
|
}
|
||||||
|
@ -652,18 +660,18 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
||||||
* If the payload list is not empty, DefaultItemAnimator returns <code>true</code>.
|
* If the payload list is not empty, DefaultItemAnimator returns <code>true</code>.
|
||||||
* When this is the case:
|
* When this is the case:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>If you override {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)}, both
|
* <li>If you override {@link #animateChange(RecyclerView.ViewHolder, RecyclerView.ViewHolder, int, int, int, int)}, both
|
||||||
* ViewHolder arguments will be the same instance.
|
* ViewHolder arguments will be the same instance.
|
||||||
* </li>
|
* </li>
|
||||||
* <li>
|
* <li>
|
||||||
* If you are not overriding {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)},
|
* If you are not overriding {@link #animateChange(RecyclerView.ViewHolder, RecyclerView.ViewHolder, int, int, int, int)},
|
||||||
* then DefaultItemAnimator will call {@link #animateMove(ViewHolder, int, int, int, int)} and
|
* then DefaultItemAnimator will call {@link #animateMove(RecyclerView.ViewHolder, int, int, int, int)} and
|
||||||
* run a move animation instead.
|
* run a move animation instead.
|
||||||
* </li>
|
* </li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
|
public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder,
|
||||||
@NonNull List<Object> payloads) {
|
@NonNull List<Object> payloads) {
|
||||||
return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads);
|
return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads);
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
* Copyright 2018 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -14,11 +14,12 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.telegram.messenger.support.util;
|
package androidx.recyclerview.widget;
|
||||||
|
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.IntRange;
|
||||||
import android.support.annotation.VisibleForTesting;
|
import androidx.annotation.NonNull;
|
||||||
import org.telegram.messenger.support.widget.RecyclerView;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -27,18 +28,23 @@ import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DiffUtil is a utility class that can calculate the difference between two lists and output a
|
* DiffUtil is a utility class that calculates the difference between two lists and outputs a
|
||||||
* list of update operations that converts the first list into the second one.
|
* list of update operations that converts the first list into the second one.
|
||||||
* <p>
|
* <p>
|
||||||
* It can be used to calculate updates for a RecyclerView Adapter. See
|
* It can be used to calculate updates for a RecyclerView Adapter. See {@link ListAdapter} and
|
||||||
* {@link android.support.v7.recyclerview.extensions.ListAdapter} and
|
* {@link AsyncListDiffer} which can simplify the use of DiffUtil on a background thread.
|
||||||
* {@link android.support.v7.recyclerview.extensions.AsyncListDiffer} which can compute diffs using
|
|
||||||
* DiffUtil on a background thread.
|
|
||||||
* <p>
|
* <p>
|
||||||
* DiffUtil uses Eugene W. Myers's difference algorithm to calculate the minimal number of updates
|
* DiffUtil uses Eugene W. Myers's difference algorithm to calculate the minimal number of updates
|
||||||
* to convert one list into another. Myers's algorithm does not handle items that are moved so
|
* to convert one list into another. Myers's algorithm does not handle items that are moved so
|
||||||
* DiffUtil runs a second pass on the result to detect items that were moved.
|
* DiffUtil runs a second pass on the result to detect items that were moved.
|
||||||
* <p>
|
* <p>
|
||||||
|
* Note that DiffUtil, ListAdapter, and AsyncListDiffer require the list to not mutate while in use.
|
||||||
|
* This generally means that both the lists themselves and their elements (or at least, the
|
||||||
|
* properties of elements used in diffing) should not be modified directly. Instead, new lists
|
||||||
|
* should be provided any time content changes. It's common for lists passed to DiffUtil to share
|
||||||
|
* elements that have not mutated, so it is not strictly required to reload all data to use
|
||||||
|
* DiffUtil.
|
||||||
|
* <p>
|
||||||
* If the lists are large, this operation may take significant time so you are advised to run this
|
* If the lists are large, this operation may take significant time so you are advised to run this
|
||||||
* on a background thread, get the {@link DiffResult} then apply it on the RecyclerView on the main
|
* on a background thread, get the {@link DiffResult} then apply it on the RecyclerView on the main
|
||||||
* thread.
|
* thread.
|
||||||
|
@ -66,7 +72,8 @@ import java.util.List;
|
||||||
* <p>
|
* <p>
|
||||||
* Due to implementation constraints, the max size of the list can be 2^26.
|
* Due to implementation constraints, the max size of the list can be 2^26.
|
||||||
*
|
*
|
||||||
* @see android.support.v7.recyclerview.extensions.AsyncListDiffer
|
* @see ListAdapter
|
||||||
|
* @see AsyncListDiffer
|
||||||
*/
|
*/
|
||||||
public class DiffUtil {
|
public class DiffUtil {
|
||||||
|
|
||||||
|
@ -93,7 +100,8 @@ public class DiffUtil {
|
||||||
* @return A DiffResult that contains the information about the edit sequence to convert the
|
* @return A DiffResult that contains the information about the edit sequence to convert the
|
||||||
* old list into the new list.
|
* old list into the new list.
|
||||||
*/
|
*/
|
||||||
public static DiffResult calculateDiff(Callback cb) {
|
@NonNull
|
||||||
|
public static DiffResult calculateDiff(@NonNull Callback cb) {
|
||||||
return calculateDiff(cb, true);
|
return calculateDiff(cb, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +118,8 @@ public class DiffUtil {
|
||||||
* @return A DiffResult that contains the information about the edit sequence to convert the
|
* @return A DiffResult that contains the information about the edit sequence to convert the
|
||||||
* old list into the new list.
|
* old list into the new list.
|
||||||
*/
|
*/
|
||||||
public static DiffResult calculateDiff(Callback cb, boolean detectMoves) {
|
@NonNull
|
||||||
|
public static DiffResult calculateDiff(@NonNull Callback cb, boolean detectMoves) {
|
||||||
final int oldSize = cb.getOldListSize();
|
final int oldSize = cb.getOldListSize();
|
||||||
final int newSize = cb.getNewListSize();
|
final int newSize = cb.getNewListSize();
|
||||||
|
|
||||||
|
@ -316,7 +325,7 @@ public class DiffUtil {
|
||||||
* DiffUtil uses this method to check equality instead of {@link Object#equals(Object)}
|
* DiffUtil uses this method to check equality instead of {@link Object#equals(Object)}
|
||||||
* so that you can change its behavior depending on your UI.
|
* so that you can change its behavior depending on your UI.
|
||||||
* For example, if you are using DiffUtil with a
|
* For example, if you are using DiffUtil with a
|
||||||
* {@link android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}, you should
|
* {@link RecyclerView.Adapter RecyclerView.Adapter}, you should
|
||||||
* return whether the items' visual representations are the same.
|
* return whether the items' visual representations are the same.
|
||||||
* <p>
|
* <p>
|
||||||
* This method is called only if {@link #areItemsTheSame(int, int)} returns
|
* This method is called only if {@link #areItemsTheSame(int, int)} returns
|
||||||
|
@ -336,7 +345,7 @@ public class DiffUtil {
|
||||||
* <p>
|
* <p>
|
||||||
* For example, if you are using DiffUtil with {@link RecyclerView}, you can return the
|
* For example, if you are using DiffUtil with {@link RecyclerView}, you can return the
|
||||||
* particular field that changed in the item and your
|
* particular field that changed in the item and your
|
||||||
* {@link android.support.v7.widget.RecyclerView.ItemAnimator ItemAnimator} can use that
|
* {@link RecyclerView.ItemAnimator ItemAnimator} can use that
|
||||||
* information to run the correct animation.
|
* information to run the correct animation.
|
||||||
* <p>
|
* <p>
|
||||||
* Default implementation returns {@code null}.
|
* Default implementation returns {@code null}.
|
||||||
|
@ -366,6 +375,10 @@ public class DiffUtil {
|
||||||
* Called to check whether two objects represent the same item.
|
* Called to check whether two objects represent the same item.
|
||||||
* <p>
|
* <p>
|
||||||
* For example, if your items have unique ids, this method should check their id equality.
|
* For example, if your items have unique ids, this method should check their id equality.
|
||||||
|
* <p>
|
||||||
|
* Note: {@code null} items in the list are assumed to be the same as another {@code null}
|
||||||
|
* item and are assumed to not be the same as a non-{@code null} item. This callback will
|
||||||
|
* not be invoked for either of those cases.
|
||||||
*
|
*
|
||||||
* @param oldItem The item in the old list.
|
* @param oldItem The item in the old list.
|
||||||
* @param newItem The item in the new list.
|
* @param newItem The item in the new list.
|
||||||
|
@ -373,7 +386,7 @@ public class DiffUtil {
|
||||||
*
|
*
|
||||||
* @see Callback#areItemsTheSame(int, int)
|
* @see Callback#areItemsTheSame(int, int)
|
||||||
*/
|
*/
|
||||||
public abstract boolean areItemsTheSame(T oldItem, T newItem);
|
public abstract boolean areItemsTheSame(@NonNull T oldItem, @NonNull T newItem);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called to check whether two items have the same data.
|
* Called to check whether two items have the same data.
|
||||||
|
@ -384,11 +397,14 @@ public class DiffUtil {
|
||||||
* change its behavior depending on your UI.
|
* change its behavior depending on your UI.
|
||||||
* <p>
|
* <p>
|
||||||
* For example, if you are using DiffUtil with a
|
* For example, if you are using DiffUtil with a
|
||||||
* {@link android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}, you should
|
* {@link RecyclerView.Adapter RecyclerView.Adapter}, you should
|
||||||
* return whether the items' visual representations are the same.
|
* return whether the items' visual representations are the same.
|
||||||
* <p>
|
* <p>
|
||||||
* This method is called only if {@link #areItemsTheSame(T, T)} returns {@code true} for
|
* This method is called only if {@link #areItemsTheSame(T, T)} returns {@code true} for
|
||||||
* these items.
|
* these items.
|
||||||
|
* <p>
|
||||||
|
* Note: Two {@code null} items are assumed to represent the same contents. This callback
|
||||||
|
* will not be invoked for this case.
|
||||||
*
|
*
|
||||||
* @param oldItem The item in the old list.
|
* @param oldItem The item in the old list.
|
||||||
* @param newItem The item in the new list.
|
* @param newItem The item in the new list.
|
||||||
|
@ -396,7 +412,7 @@ public class DiffUtil {
|
||||||
*
|
*
|
||||||
* @see Callback#areContentsTheSame(int, int)
|
* @see Callback#areContentsTheSame(int, int)
|
||||||
*/
|
*/
|
||||||
public abstract boolean areContentsTheSame(T oldItem, T newItem);
|
public abstract boolean areContentsTheSame(@NonNull T oldItem, @NonNull T newItem);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When {@link #areItemsTheSame(T, T)} returns {@code true} for two items and
|
* When {@link #areItemsTheSame(T, T)} returns {@code true} for two items and
|
||||||
|
@ -405,7 +421,7 @@ public class DiffUtil {
|
||||||
* <p>
|
* <p>
|
||||||
* For example, if you are using DiffUtil with {@link RecyclerView}, you can return the
|
* For example, if you are using DiffUtil with {@link RecyclerView}, you can return the
|
||||||
* particular field that changed in the item and your
|
* particular field that changed in the item and your
|
||||||
* {@link android.support.v7.widget.RecyclerView.ItemAnimator ItemAnimator} can use that
|
* {@link RecyclerView.ItemAnimator ItemAnimator} can use that
|
||||||
* information to run the correct animation.
|
* information to run the correct animation.
|
||||||
* <p>
|
* <p>
|
||||||
* Default implementation returns {@code null}.
|
* Default implementation returns {@code null}.
|
||||||
|
@ -413,7 +429,8 @@ public class DiffUtil {
|
||||||
* @see Callback#getChangePayload(int, int)
|
* @see Callback#getChangePayload(int, int)
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||||
public Object getChangePayload(T oldItem, T newItem) {
|
@Nullable
|
||||||
|
public Object getChangePayload(@NonNull T oldItem, @NonNull T newItem) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -482,6 +499,12 @@ public class DiffUtil {
|
||||||
* {@link RecyclerView.Adapter} via {@link #dispatchUpdatesTo(RecyclerView.Adapter)}.
|
* {@link RecyclerView.Adapter} via {@link #dispatchUpdatesTo(RecyclerView.Adapter)}.
|
||||||
*/
|
*/
|
||||||
public static class DiffResult {
|
public static class DiffResult {
|
||||||
|
/**
|
||||||
|
* Signifies an item not present in the list.
|
||||||
|
*/
|
||||||
|
public static final int NO_POSITION = -1;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* While reading the flags below, keep in mind that when multiple items move in a list,
|
* While reading the flags below, keep in mind that when multiple items move in a list,
|
||||||
* Myers's may pick any of them as the anchor item and consider that one NOT_CHANGED while
|
* Myers's may pick any of them as the anchor item and consider that one NOT_CHANGED while
|
||||||
|
@ -633,6 +656,54 @@ public class DiffUtil {
|
||||||
findMatchingItem(x, y, snakeIndex, true);
|
findMatchingItem(x, y, snakeIndex, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a position in the old list, returns the position in the new list, or
|
||||||
|
* {@code NO_POSITION} if it was removed.
|
||||||
|
*
|
||||||
|
* @param oldListPosition Position of item in old list
|
||||||
|
*
|
||||||
|
* @return Position of item in new list, or {@code NO_POSITION} if not present.
|
||||||
|
*
|
||||||
|
* @see #NO_POSITION
|
||||||
|
* @see #convertNewPositionToOld(int)
|
||||||
|
*/
|
||||||
|
public int convertOldPositionToNew(@IntRange(from = 0) int oldListPosition) {
|
||||||
|
if (oldListPosition < 0 || oldListPosition >= mOldListSize) {
|
||||||
|
throw new IndexOutOfBoundsException("Index out of bounds - passed position = "
|
||||||
|
+ oldListPosition + ", old list size = " + mOldListSize);
|
||||||
|
}
|
||||||
|
final int status = mOldItemStatuses[oldListPosition];
|
||||||
|
if ((status & FLAG_MASK) == 0) {
|
||||||
|
return NO_POSITION;
|
||||||
|
} else {
|
||||||
|
return status >> FLAG_OFFSET;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a position in the new list, returns the position in the old list, or
|
||||||
|
* {@code NO_POSITION} if it was removed.
|
||||||
|
*
|
||||||
|
* @param newListPosition Position of item in new list
|
||||||
|
*
|
||||||
|
* @return Position of item in old list, or {@code NO_POSITION} if not present.
|
||||||
|
*
|
||||||
|
* @see #NO_POSITION
|
||||||
|
* @see #convertOldPositionToNew(int)
|
||||||
|
*/
|
||||||
|
public int convertNewPositionToOld(@IntRange(from = 0) int newListPosition) {
|
||||||
|
if (newListPosition < 0 || newListPosition >= mNewListSize) {
|
||||||
|
throw new IndexOutOfBoundsException("Index out of bounds - passed position = "
|
||||||
|
+ newListPosition + ", new list size = " + mNewListSize);
|
||||||
|
}
|
||||||
|
final int status = mNewItemStatuses[newListPosition];
|
||||||
|
if ((status & FLAG_MASK) == 0) {
|
||||||
|
return NO_POSITION;
|
||||||
|
} else {
|
||||||
|
return status >> FLAG_OFFSET;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds a matching item that is before the given coordinates in the matrix
|
* Finds a matching item that is before the given coordinates in the matrix
|
||||||
* (before : left and above).
|
* (before : left and above).
|
||||||
|
@ -698,7 +769,7 @@ public class DiffUtil {
|
||||||
/**
|
/**
|
||||||
* Dispatches the update events to the given adapter.
|
* Dispatches the update events to the given adapter.
|
||||||
* <p>
|
* <p>
|
||||||
* For example, if you have an {@link android.support.v7.widget.RecyclerView.Adapter Adapter}
|
* For example, if you have an {@link RecyclerView.Adapter Adapter}
|
||||||
* that is backed by a {@link List}, you can swap the list with the new one then call this
|
* that is backed by a {@link List}, you can swap the list with the new one then call this
|
||||||
* method to dispatch all updates to the RecyclerView.
|
* method to dispatch all updates to the RecyclerView.
|
||||||
* <pre>
|
* <pre>
|
||||||
|
@ -714,11 +785,11 @@ public class DiffUtil {
|
||||||
* before RecyclerView tries to read it.
|
* before RecyclerView tries to read it.
|
||||||
* <p>
|
* <p>
|
||||||
* On the other hand, if you have another
|
* On the other hand, if you have another
|
||||||
* {@link android.support.v7.widget.RecyclerView.AdapterDataObserver AdapterDataObserver}
|
* {@link RecyclerView.AdapterDataObserver AdapterDataObserver}
|
||||||
* that tries to process events synchronously, this may confuse that observer because the
|
* that tries to process events synchronously, this may confuse that observer because the
|
||||||
* list is instantly moved to its final state while the adapter updates are dispatched later
|
* list is instantly moved to its final state while the adapter updates are dispatched later
|
||||||
* on, one by one. If you have such an
|
* on, one by one. If you have such an
|
||||||
* {@link android.support.v7.widget.RecyclerView.AdapterDataObserver AdapterDataObserver},
|
* {@link RecyclerView.AdapterDataObserver AdapterDataObserver},
|
||||||
* you can use
|
* you can use
|
||||||
* {@link #dispatchUpdatesTo(ListUpdateCallback)} to handle each modification
|
* {@link #dispatchUpdatesTo(ListUpdateCallback)} to handle each modification
|
||||||
* manually.
|
* manually.
|
||||||
|
@ -727,7 +798,7 @@ public class DiffUtil {
|
||||||
* displaying the new list.
|
* displaying the new list.
|
||||||
* @see AdapterListUpdateCallback
|
* @see AdapterListUpdateCallback
|
||||||
*/
|
*/
|
||||||
public void dispatchUpdatesTo(final RecyclerView.Adapter adapter) {
|
public void dispatchUpdatesTo(@NonNull final RecyclerView.Adapter adapter) {
|
||||||
dispatchUpdatesTo(new AdapterListUpdateCallback(adapter));
|
dispatchUpdatesTo(new AdapterListUpdateCallback(adapter));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -740,7 +811,7 @@ public class DiffUtil {
|
||||||
* @param updateCallback The callback to receive the update operations.
|
* @param updateCallback The callback to receive the update operations.
|
||||||
* @see #dispatchUpdatesTo(RecyclerView.Adapter)
|
* @see #dispatchUpdatesTo(RecyclerView.Adapter)
|
||||||
*/
|
*/
|
||||||
public void dispatchUpdatesTo(ListUpdateCallback updateCallback) {
|
public void dispatchUpdatesTo(@NonNull ListUpdateCallback updateCallback) {
|
||||||
final BatchingListUpdateCallback batchingCallback;
|
final BatchingListUpdateCallback batchingCallback;
|
||||||
if (updateCallback instanceof BatchingListUpdateCallback) {
|
if (updateCallback instanceof BatchingListUpdateCallback) {
|
||||||
batchingCallback = (BatchingListUpdateCallback) updateCallback;
|
batchingCallback = (BatchingListUpdateCallback) updateCallback;
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
* Copyright 2018 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,18 +15,20 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
package org.telegram.messenger.support.widget;
|
package androidx.recyclerview.widget;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DividerItemDecoration is a {@link RecyclerView.ItemDecoration} that can be used as a divider
|
* DividerItemDecoration is a {@link RecyclerView.ItemDecoration} that can be used as a divider
|
||||||
* between items of a {@link LinearLayoutManager}. It supports both {@link #HORIZONTAL} and
|
* between items of a {@link LinearLayoutManager}. It supports both {@link #HORIZONTAL} and
|
||||||
|
@ -98,6 +100,14 @@ public class DividerItemDecoration extends RecyclerView.ItemDecoration {
|
||||||
mDivider = drawable;
|
mDivider = drawable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the {@link Drawable} for this divider.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public Drawable getDrawable() {
|
||||||
|
return mDivider;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
|
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
|
||||||
if (parent.getLayoutManager() == null || mDivider == null) {
|
if (parent.getLayoutManager() == null || mDivider == null) {
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2017 The Android Open Source Project
|
* Copyright 2018 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.telegram.messenger.support.widget;
|
package androidx.recyclerview.widget;
|
||||||
|
|
||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.animation.AnimatorListenerAdapter;
|
import android.animation.AnimatorListenerAdapter;
|
||||||
|
@ -23,15 +23,14 @@ import android.animation.ValueAnimator.AnimatorUpdateListener;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.StateListDrawable;
|
import android.graphics.drawable.StateListDrawable;
|
||||||
import android.support.annotation.IntDef;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.annotation.VisibleForTesting;
|
|
||||||
import android.support.v4.view.ViewCompat;
|
|
||||||
import org.telegram.messenger.support.widget.RecyclerView.ItemDecoration;
|
|
||||||
import org.telegram.messenger.support.widget.RecyclerView.OnItemTouchListener;
|
|
||||||
import org.telegram.messenger.support.widget.RecyclerView.OnScrollListener;
|
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
|
||||||
|
import androidx.annotation.IntDef;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
@ -39,7 +38,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||||
* Class responsible to animate and provide a fast scroller.
|
* Class responsible to animate and provide a fast scroller.
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
class FastScroller extends ItemDecoration implements OnItemTouchListener {
|
class FastScroller extends RecyclerView.ItemDecoration implements RecyclerView.OnItemTouchListener {
|
||||||
@IntDef({STATE_HIDDEN, STATE_VISIBLE, STATE_DRAGGING})
|
@IntDef({STATE_HIDDEN, STATE_VISIBLE, STATE_DRAGGING})
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
private @interface State { }
|
private @interface State { }
|
||||||
|
@ -79,8 +78,10 @@ class FastScroller extends ItemDecoration implements OnItemTouchListener {
|
||||||
private final int mMargin;
|
private final int mMargin;
|
||||||
|
|
||||||
// Final values for the vertical scroll bar
|
// Final values for the vertical scroll bar
|
||||||
private final StateListDrawable mVerticalThumbDrawable;
|
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||||
private final Drawable mVerticalTrackDrawable;
|
final StateListDrawable mVerticalThumbDrawable;
|
||||||
|
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||||
|
final Drawable mVerticalTrackDrawable;
|
||||||
private final int mVerticalThumbWidth;
|
private final int mVerticalThumbWidth;
|
||||||
private final int mVerticalTrackWidth;
|
private final int mVerticalTrackWidth;
|
||||||
|
|
||||||
|
@ -115,15 +116,18 @@ class FastScroller extends ItemDecoration implements OnItemTouchListener {
|
||||||
|
|
||||||
private final int[] mVerticalRange = new int[2];
|
private final int[] mVerticalRange = new int[2];
|
||||||
private final int[] mHorizontalRange = new int[2];
|
private final int[] mHorizontalRange = new int[2];
|
||||||
private final ValueAnimator mShowHideAnimator = ValueAnimator.ofFloat(0, 1);
|
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||||
@AnimationState private int mAnimationState = ANIMATION_STATE_OUT;
|
final ValueAnimator mShowHideAnimator = ValueAnimator.ofFloat(0, 1);
|
||||||
|
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||||
|
@AnimationState int mAnimationState = ANIMATION_STATE_OUT;
|
||||||
private final Runnable mHideRunnable = new Runnable() {
|
private final Runnable mHideRunnable = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
hide(HIDE_DURATION_MS);
|
hide(HIDE_DURATION_MS);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private final OnScrollListener mOnScrollListener = new OnScrollListener() {
|
private final RecyclerView.OnScrollListener
|
||||||
|
mOnScrollListener = new RecyclerView.OnScrollListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
||||||
updateScrollPosition(recyclerView.computeHorizontalScrollOffset(),
|
updateScrollPosition(recyclerView.computeHorizontalScrollOffset(),
|
||||||
|
@ -182,11 +186,12 @@ class FastScroller extends ItemDecoration implements OnItemTouchListener {
|
||||||
cancelHide();
|
cancelHide();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void requestRedraw() {
|
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||||
|
void requestRedraw() {
|
||||||
mRecyclerView.invalidate();
|
mRecyclerView.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setState(@State int state) {
|
void setState(@State int state) {
|
||||||
if (state == STATE_DRAGGING && mState != STATE_DRAGGING) {
|
if (state == STATE_DRAGGING && mState != STATE_DRAGGING) {
|
||||||
mVerticalThumbDrawable.setState(PRESSED_STATE_SET);
|
mVerticalThumbDrawable.setState(PRESSED_STATE_SET);
|
||||||
cancelHide();
|
cancelHide();
|
||||||
|
@ -219,11 +224,6 @@ class FastScroller extends ItemDecoration implements OnItemTouchListener {
|
||||||
return mState == STATE_VISIBLE;
|
return mState == STATE_VISIBLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting boolean isHidden() {
|
|
||||||
return mState == STATE_HIDDEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void show() {
|
public void show() {
|
||||||
switch (mAnimationState) {
|
switch (mAnimationState) {
|
||||||
case ANIMATION_STATE_FADING_OUT:
|
case ANIMATION_STATE_FADING_OUT:
|
||||||
|
@ -239,10 +239,6 @@ class FastScroller extends ItemDecoration implements OnItemTouchListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void hide() {
|
|
||||||
hide(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void hide(int duration) {
|
void hide(int duration) {
|
||||||
switch (mAnimationState) {
|
switch (mAnimationState) {
|
||||||
|
@ -379,7 +375,8 @@ class FastScroller extends ItemDecoration implements OnItemTouchListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent ev) {
|
public boolean onInterceptTouchEvent(@NonNull RecyclerView recyclerView,
|
||||||
|
@NonNull MotionEvent ev) {
|
||||||
final boolean handled;
|
final boolean handled;
|
||||||
if (mState == STATE_VISIBLE) {
|
if (mState == STATE_VISIBLE) {
|
||||||
boolean insideVerticalThumb = isPointInsideVerticalThumb(ev.getX(), ev.getY());
|
boolean insideVerticalThumb = isPointInsideVerticalThumb(ev.getX(), ev.getY());
|
||||||
|
@ -408,7 +405,7 @@ class FastScroller extends ItemDecoration implements OnItemTouchListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTouchEvent(RecyclerView recyclerView, MotionEvent me) {
|
public void onTouchEvent(@NonNull RecyclerView recyclerView, @NonNull MotionEvent me) {
|
||||||
if (mState == STATE_HIDDEN) {
|
if (mState == STATE_HIDDEN) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -551,6 +548,9 @@ class FastScroller extends ItemDecoration implements OnItemTouchListener {
|
||||||
|
|
||||||
private boolean mCanceled = false;
|
private boolean mCanceled = false;
|
||||||
|
|
||||||
|
AnimatorListener() {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationEnd(Animator animation) {
|
public void onAnimationEnd(Animator animation) {
|
||||||
// Cancel is always followed by a new directive, so don't update state.
|
// Cancel is always followed by a new directive, so don't update state.
|
||||||
|
@ -574,6 +574,8 @@ class FastScroller extends ItemDecoration implements OnItemTouchListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AnimatorUpdater implements AnimatorUpdateListener {
|
private class AnimatorUpdater implements AnimatorUpdateListener {
|
||||||
|
AnimatorUpdater() {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationUpdate(ValueAnimator valueAnimator) {
|
public void onAnimationUpdate(ValueAnimator valueAnimator) {
|