Update to 5.6.1
|
@ -10,23 +10,26 @@ configurations {
|
|||
compile.exclude module: 'support-v4'
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
exclude group: 'com.google.firebase', module: 'firebase-core'
|
||||
}
|
||||
|
||||
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-compat-qual:2.5.0'
|
||||
implementation 'com.google.firebase:firebase-core:16.0.7'
|
||||
implementation 'com.google.firebase:firebase-messaging:17.3.4'
|
||||
implementation 'com.google.firebase:firebase-config:16.1.3'
|
||||
implementation 'com.google.android.gms:play-services-maps:16.0.0'
|
||||
implementation 'com.google.firebase:firebase-messaging:18.0.0'
|
||||
implementation 'com.google.firebase:firebase-config:16.5.0'
|
||||
implementation 'com.google.android.gms:play-services-maps:16.1.0'
|
||||
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-wallet: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 'com.googlecode.mp4parser:isoparser:1.0.6'
|
||||
implementation 'com.stripe:stripe-android:2.0.2'
|
||||
|
@ -90,10 +93,10 @@ android {
|
|||
initWith debug
|
||||
minifyEnabled false
|
||||
multiDexEnabled true
|
||||
dependencies{
|
||||
dependencies {
|
||||
implementation 'com.android.support:multidex:1.0.3'
|
||||
}
|
||||
manifestPlaceholders = [applicationClassName:"MultiDexApplicationLoader"]
|
||||
manifestPlaceholders = [applicationClassName: "MultiDexApplicationLoader"]
|
||||
}
|
||||
|
||||
HA {
|
||||
|
@ -115,8 +118,6 @@ android {
|
|||
}
|
||||
}
|
||||
|
||||
defaultConfig.versionCode = 1517
|
||||
|
||||
sourceSets.debug {
|
||||
manifest.srcFile 'config/debug/AndroidManifest.xml'
|
||||
}
|
||||
|
@ -242,12 +243,24 @@ android {
|
|||
}
|
||||
}
|
||||
|
||||
defaultConfig.versionCode = 1591
|
||||
|
||||
applicationVariants.all { variant ->
|
||||
variant.outputs.all { output ->
|
||||
output.processManifest.doLast {
|
||||
output.getProcessManifestProvider().get().doLast {
|
||||
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()
|
||||
|
||||
manifestContent = manifestContent.replace(String.format('android:versionCode="%d"', defaultConfig.versionCode), String.format('android:versionCode="%s"', defaultConfig.versionCode * 10 + abiVersion))
|
||||
file(manifestPath).write(manifestContent)
|
||||
}
|
||||
|
@ -256,7 +269,7 @@ android {
|
|||
|
||||
variantFilter { variant ->
|
||||
def names = variant.flavors*.name
|
||||
if(variant.buildType.name!="release" && !names.contains("afat")){
|
||||
if (variant.buildType.name != "release" && !names.contains("afat")) {
|
||||
setIgnore(true)
|
||||
}
|
||||
}
|
||||
|
@ -264,7 +277,7 @@ android {
|
|||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 27
|
||||
versionName "5.4.0"
|
||||
versionName "5.6.1"
|
||||
|
||||
vectorDrawables.generatedDensities = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi']
|
||||
|
||||
|
@ -275,7 +288,7 @@ android {
|
|||
}
|
||||
}
|
||||
|
||||
manifestPlaceholders = [applicationClassName:"ApplicationLoader"]
|
||||
manifestPlaceholders = [applicationClassName: "ApplicationLoader"]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,11 +19,10 @@
|
|||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:roundIcon="@drawable/ic_launcher"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:label="@string/AppNameBeta"
|
||||
android:theme="@style/Theme.TMessages.Start"
|
||||
android:name=".ApplicationLoader"
|
||||
android:hardwareAccelerated="@bool/useHardwareAcceleration"
|
||||
android:largeHeap="true"
|
||||
android:supportsRtl="false"
|
||||
|
@ -40,6 +39,8 @@
|
|||
|
||||
<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
|
||||
tools:replace="android:enabled"
|
||||
|
|
|
@ -15,13 +15,14 @@
|
|||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<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"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:roundIcon="@drawable/ic_launcher"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:label="@string/AppNameBeta"
|
||||
android:theme="@style/Theme.TMessages.Start"
|
||||
android:name=".ApplicationLoader"
|
||||
|
@ -41,6 +42,8 @@
|
|||
|
||||
<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
|
||||
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
|
||||
android:allowBackup="false"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:roundIcon="@drawable/ic_launcher"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:label="@string/AppName"
|
||||
android:theme="@style/Theme.TMessages.Start"
|
||||
android:name=".ApplicationLoader"
|
||||
|
@ -40,6 +40,9 @@
|
|||
|
||||
<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
|
||||
tools:replace="android:enabled"
|
||||
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_FINE_LOCATION" />
|
||||
<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"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:roundIcon="@drawable/ic_launcher"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:label="@string/AppName"
|
||||
android:theme="@style/Theme.TMessages.Start"
|
||||
android:name=".ApplicationLoader"
|
||||
|
@ -41,6 +42,9 @@
|
|||
|
||||
<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
|
||||
tools:replace="android:enabled"
|
||||
android:name="com.google.android.gms.measurement.AppMeasurementReceiver"
|
||||
|
|
|
@ -95,6 +95,7 @@ endif
|
|||
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
TGVOIP_ADDITIONAL_CFLAGS := -DTGVOIP_NO_VIDEO
|
||||
include $(MY_LOCAL_PATH)/libtgvoip/Android.mk
|
||||
LOCAL_PATH := $(MY_LOCAL_PATH) # restore local path after include
|
||||
|
||||
|
@ -123,7 +124,6 @@ LOCAL_SRC_FILES := \
|
|||
./tgnet/Request.cpp \
|
||||
./tgnet/Timer.cpp \
|
||||
./tgnet/TLObject.cpp \
|
||||
./tgnet/FileLoadOperation.cpp \
|
||||
./tgnet/ProxyCheckInfo.cpp \
|
||||
./tgnet/Handshake.cpp \
|
||||
./tgnet/Config.cpp
|
||||
|
@ -547,8 +547,6 @@ LOCAL_SRC_FILES += \
|
|||
./SqliteWrapper.cpp \
|
||||
./TgNetWrapper.cpp \
|
||||
./NativeLoader.cpp \
|
||||
./emoji/emoji_suggestions_data.cpp \
|
||||
./emoji/emoji_suggestions.cpp \
|
||||
./exoplayer/flac_jni.cc \
|
||||
./exoplayer/flac_parser.cc \
|
||||
./exoplayer/opus_jni.cc \
|
||||
|
|
|
@ -163,15 +163,20 @@ jlong Java_org_telegram_SQLite_SQLiteDatabase_opendb(JNIEnv *env, jobject object
|
|||
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) {
|
||||
sqlite3_stmt *handle = (sqlite3_stmt *) (intptr_t) statementHandle;
|
||||
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;
|
||||
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) {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "tgnet/NativeByteBuffer.h"
|
||||
#include "tgnet/ConnectionsManager.h"
|
||||
#include "tgnet/MTProtoScheme.h"
|
||||
#include "tgnet/FileLoadOperation.h"
|
||||
#include "tgnet/ConnectionSocket.h"
|
||||
|
||||
JavaVM *java;
|
||||
jclass jclass_RequestDelegateInternal;
|
||||
|
@ -19,11 +19,6 @@ jmethodID jclass_QuickAckDelegate_run;
|
|||
jclass jclass_WriteToSocketDelegate;
|
||||
jmethodID jclass_WriteToSocketDelegate_run;
|
||||
|
||||
jclass jclass_FileLoadOperationDelegate;
|
||||
jmethodID jclass_FileLoadOperationDelegate_onFinished;
|
||||
jmethodID jclass_FileLoadOperationDelegate_onFailed;
|
||||
jmethodID jclass_FileLoadOperationDelegate_onProgressChanged;
|
||||
|
||||
jclass jclass_ConnectionsManager;
|
||||
jmethodID jclass_ConnectionsManager_onUnparsedMessageReceived;
|
||||
jmethodID jclass_ConnectionsManager_onUpdate;
|
||||
|
@ -41,93 +36,6 @@ jmethodID jclass_ConnectionsManager_getInitFlags;
|
|||
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
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 address = (jstring) jniEnv[instanceNum]->CallStaticObjectMethod(jclass_ConnectionsManager, jclass_ConnectionsManager_getHostByName, domainName, instanceNum);
|
||||
const char *addressStr = jniEnv[instanceNum]->GetStringUTFChars(address, 0);
|
||||
std::string result = std::string(addressStr);
|
||||
if (addressStr != 0) {
|
||||
jniEnv[instanceNum]->ReleaseStringUTFChars(address, addressStr);
|
||||
}
|
||||
jniEnv[instanceNum]->CallStaticVoidMethod(jclass_ConnectionsManager, jclass_ConnectionsManager_getHostByName, domainName, (jlong) (intptr_t) socket);
|
||||
jniEnv[instanceNum]->DeleteLocalRef(domainName);
|
||||
jniEnv[instanceNum]->DeleteLocalRef(address);
|
||||
return result;
|
||||
}
|
||||
|
||||
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) {
|
||||
const char *langCodeStr = env->GetStringUTFChars(langCode, 0);
|
||||
|
||||
|
@ -506,7 +422,8 @@ static JNINativeMethod ConnectionsManagerMethods[] = {
|
|||
{"native_setPushConnectionEnabled", "(IZ)V", (void *) setPushConnectionEnabled},
|
||||
{"native_setJava", "(Z)V", (void *) setJava},
|
||||
{"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) {
|
||||
|
@ -528,10 +445,6 @@ extern "C" int registerNativeTgNetFunctions(JavaVM *vm, JNIEnv *env) {
|
|||
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]))) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
@ -571,27 +484,6 @@ extern "C" int registerNativeTgNetFunctions(JavaVM *vm, JNIEnv *env) {
|
|||
if (jclass_WriteToSocketDelegate_run == 0) {
|
||||
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"));
|
||||
if (jclass_ConnectionsManager == 0) {
|
||||
return JNI_FALSE;
|
||||
|
@ -640,7 +532,7 @@ extern "C" int registerNativeTgNetFunctions(JavaVM *vm, JNIEnv *env) {
|
|||
if (jclass_ConnectionsManager_onProxyError == 0) {
|
||||
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) {
|
||||
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" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavformat/isom.h>
|
||||
#include <libavutil/eval.h>
|
||||
#include <libswscale/swscale.h>
|
||||
|
||||
|
@ -67,11 +68,10 @@ typedef struct VideoInfo {
|
|||
}
|
||||
stream = nullptr;
|
||||
}
|
||||
/*if (ioBuffer) { TODO memleak?
|
||||
av_free(ioBuffer);
|
||||
ioBuffer = nullptr;
|
||||
}*/
|
||||
if (ioContext != nullptr) {
|
||||
if (ioContext->buffer) {
|
||||
av_freep(&ioContext->buffer);
|
||||
}
|
||||
avio_context_free(&ioContext);
|
||||
ioContext = nullptr;
|
||||
}
|
||||
|
@ -88,12 +88,14 @@ typedef struct VideoInfo {
|
|||
|
||||
video_stream_idx = -1;
|
||||
video_stream = nullptr;
|
||||
audio_stream = nullptr;
|
||||
}
|
||||
|
||||
AVFormatContext *fmt_ctx = nullptr;
|
||||
char *src = nullptr;
|
||||
int video_stream_idx = -1;
|
||||
AVStream *video_stream = nullptr;
|
||||
AVStream *audio_stream = nullptr;
|
||||
AVCodecContext *video_dec_ctx = nullptr;
|
||||
AVFrame *frame = nullptr;
|
||||
bool has_decoded_frames = false;
|
||||
|
@ -248,6 +250,94 @@ int64_t seekCallback(void *opaque, int64_t offset, int whence) {
|
|||
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) {
|
||||
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];
|
||||
}
|
||||
|
||||
if (info->video_stream <= 0) {
|
||||
if (info->video_stream == nullptr) {
|
||||
LOGE("can't find video stream in the input, aborting %s", info->src);
|
||||
delete info;
|
||||
return 0;
|
||||
|
@ -442,12 +532,7 @@ void Java_org_telegram_ui_Components_AnimatedFileDrawable_seekToMs(JNIEnv *env,
|
|||
}
|
||||
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) {
|
||||
int64_t pkt_pts;
|
||||
if (info->frame->pts != AV_NOPTS_VALUE) {
|
||||
pkt_pts = info->frame->pts;
|
||||
} else {
|
||||
pkt_pts = info->frame->pkt_dts;
|
||||
}
|
||||
int64_t pkt_pts = info->frame->best_effort_timestamp;
|
||||
if (pkt_pts >= pts) {
|
||||
return;
|
||||
}
|
||||
|
@ -524,11 +609,7 @@ jint Java_org_telegram_ui_Components_AnimatedFileDrawable_getVideoFrame(JNIEnv *
|
|||
if (dataArr != nullptr) {
|
||||
wantedWidth = dataArr[0];
|
||||
wantedHeight = dataArr[1];
|
||||
if (info->frame->pts != AV_NOPTS_VALUE) {
|
||||
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));
|
||||
}
|
||||
dataArr[3] = (jint) (1000 * info->frame->best_effort_timestamp * av_q2d(info->video_stream->time_base));
|
||||
env->ReleaseIntArrayElements(data, dataArr, 0);
|
||||
} else {
|
||||
AndroidBitmapInfo bitmapInfo;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit ce74c9216f599874571061f39c2dc31632b3004b
|
||||
Subproject commit 78decc81bf25cf36ad1b4a9398aa11cb195db9c5
|
|
@ -123,9 +123,9 @@ extern "C" {
|
|||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||
** [sqlite_version()] and [sqlite_source_id()].
|
||||
*/
|
||||
#define SQLITE_VERSION "3.27.2"
|
||||
#define SQLITE_VERSION_NUMBER 3027002
|
||||
#define SQLITE_SOURCE_ID "2019-02-25 16:06:06 bd49a8271d650fa89e446b42e513b595a717b9212c91dd384aab871fc1d0f6d7"
|
||||
#define SQLITE_VERSION "3.28.0"
|
||||
#define SQLITE_VERSION_NUMBER 3028000
|
||||
#define SQLITE_SOURCE_ID "2019-04-16 19:49:53 884b4b7e502b4e991677b53971277adfaf0a04a284f8e483e2553d0f83156b50"
|
||||
|
||||
/*
|
||||
** CAPI3REF: Run-Time Library Version Numbers
|
||||
|
@ -189,6 +189,9 @@ SQLITE_API int sqlite3_libversion_number(void);
|
|||
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
|
||||
SQLITE_API int sqlite3_compileoption_used(const char *zOptName);
|
||||
SQLITE_API const char *sqlite3_compileoption_get(int N);
|
||||
#else
|
||||
# define sqlite3_compileoption_used(X) 0
|
||||
# define sqlite3_compileoption_get(X) ((void*)0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -2086,8 +2089,8 @@ struct sqlite3_mem_methods {
|
|||
**
|
||||
** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
|
||||
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
|
||||
** <dd> ^This option is used to enable or disable the two-argument
|
||||
** version of the [fts3_tokenizer()] function which is part of the
|
||||
** <dd> ^This option is used to enable or disable the
|
||||
** [fts3_tokenizer()] function which is part of the
|
||||
** [FTS3] full-text search engine extension.
|
||||
** There should be two additional arguments.
|
||||
** 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].
|
||||
** </ul>
|
||||
** </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>
|
||||
*/
|
||||
#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_RESET_DATABASE 1009 /* 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
|
||||
|
@ -3894,6 +3909,18 @@ SQLITE_API const char *sqlite3_normalized_sql(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
|
||||
** METHOD: sqlite3_stmt
|
||||
|
@ -4033,7 +4060,9 @@ typedef struct sqlite3_context sqlite3_context;
|
|||
** ^The fifth argument to the BLOB and string binding interfaces
|
||||
** is a destructor used to dispose of the BLOB or
|
||||
** string after SQLite has finished with it. ^The destructor is called
|
||||
** to dispose of the BLOB or string even if the call to 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
|
||||
** the special value [SQLITE_STATIC], then SQLite assumes that the
|
||||
** 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>
|
||||
** <td>→ <td>True if the column is unchanged in an UPDATE
|
||||
** against a virtual table.
|
||||
** <tr><td><b>sqlite3_value_frombind </b>
|
||||
** <td>→ <td>True if value originated from a [bound parameter]
|
||||
** </table></blockquote>
|
||||
**
|
||||
** <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
|
||||
** 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
|
||||
** from [sqlite3_value_blob()], [sqlite3_value_text()], or
|
||||
** [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_numeric_type(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
|
||||
|
@ -5791,7 +5828,7 @@ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
|
|||
** 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
|
||||
** 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
|
||||
** 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
|
||||
** of the changeset rebased rebased according to the configuration of the
|
||||
** 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
|
||||
** responsibility of the caller to eventually free the new buffer using
|
||||
** 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
|
||||
** "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
|
||||
** 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 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
|
||||
** 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
|
||||
** xDelete parameter was not NULL, it is invoked on the auxiliary data
|
||||
** pointer before returning.
|
||||
|
|
|
@ -206,6 +206,7 @@ void TL_config::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &
|
|||
tmp_sessions = 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_ring_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(pinned_dialogs_count_max);
|
||||
stream->writeInt32(pinned_infolder_count_max);
|
||||
stream->writeInt32(call_receive_timeout_ms);
|
||||
stream->writeInt32(call_ring_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 *result = nullptr;
|
||||
switch (constructor) {
|
||||
case 0x91d11eb:
|
||||
result = new TL_fileLocation();
|
||||
break;
|
||||
case 0x7c596b46:
|
||||
result = new TL_fileLocationUnavailable();
|
||||
break;
|
||||
case 0x55555554:
|
||||
result = new TL_fileEncryptedLocation();
|
||||
case 0xbc7fc6cd:
|
||||
result = new TL_fileLocationToBeDeprecated();
|
||||
break;
|
||||
default:
|
||||
error = true;
|
||||
|
@ -588,53 +584,15 @@ FileLocation *FileLocation::TLdeserialize(NativeByteBuffer *stream, uint32_t con
|
|||
return result;
|
||||
}
|
||||
|
||||
void TL_fileLocation::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
||||
dc_id = stream->readInt32(&error);
|
||||
void TL_fileLocationToBeDeprecated::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
||||
volume_id = stream->readInt64(&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) {
|
||||
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) {
|
||||
void TL_fileLocationToBeDeprecated::serializeToStream(NativeByteBuffer *stream) {
|
||||
stream->writeInt32(constructor);
|
||||
stream->writeInt64(volume_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) {
|
||||
|
@ -643,7 +601,7 @@ UserProfilePhoto *UserProfilePhoto::TLdeserialize(NativeByteBuffer *stream, uint
|
|||
case 0x4f11bae1:
|
||||
result = new TL_userProfilePhotoEmpty();
|
||||
break;
|
||||
case 0xd559d8c8:
|
||||
case 0xecd75d8c:
|
||||
result = new TL_userProfilePhoto();
|
||||
break;
|
||||
default:
|
||||
|
@ -663,6 +621,7 @@ void TL_userProfilePhoto::readParams(NativeByteBuffer *stream, int32_t instanceN
|
|||
photo_id = stream->readInt64(&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));
|
||||
dc_id = stream->readInt32(&error);
|
||||
}
|
||||
|
||||
void TL_userProfilePhoto::serializeToStream(NativeByteBuffer *stream) {
|
||||
|
@ -670,263 +629,9 @@ void TL_userProfilePhoto::serializeToStream(NativeByteBuffer *stream) {
|
|||
stream->writeInt64(photo_id);
|
||||
photo_small->serializeToStream(stream);
|
||||
photo_big->serializeToStream(stream);
|
||||
}
|
||||
|
||||
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);
|
||||
stream->writeInt32(dc_id);
|
||||
}
|
||||
|
||||
void TL_updatesTooLong::serializeToStream(NativeByteBuffer *stream) {
|
||||
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 {
|
||||
|
||||
public:
|
||||
static const uint32_t constructor = 0xe6ca25f6;
|
||||
static const uint32_t constructor = 0x330b4067;
|
||||
|
||||
int32_t flags;
|
||||
int32_t date;
|
||||
|
@ -128,6 +128,7 @@ public:
|
|||
int32_t channels_read_media_period;
|
||||
int32_t tmp_sessions;
|
||||
int32_t pinned_dialogs_count_max;
|
||||
int32_t pinned_infolder_count_max;
|
||||
int32_t call_receive_timeout_ms;
|
||||
int32_t call_ring_timeout_ms;
|
||||
int32_t call_connect_timeout_ms;
|
||||
|
@ -234,39 +235,16 @@ public:
|
|||
class FileLocation : public TLObject {
|
||||
|
||||
public:
|
||||
int32_t dc_id;
|
||||
int64_t volume_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);
|
||||
};
|
||||
|
||||
class TL_fileLocation : public FileLocation {
|
||||
class TL_fileLocationToBeDeprecated : public FileLocation {
|
||||
|
||||
public:
|
||||
static const uint32_t constructor = 0x91d11eb;
|
||||
|
||||
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;
|
||||
static const uint32_t constructor = 0xbc7fc6cd;
|
||||
|
||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
||||
void serializeToStream(NativeByteBuffer *stream);
|
||||
|
@ -278,6 +256,7 @@ public:
|
|||
int64_t photo_id;
|
||||
std::unique_ptr<FileLocation> photo_small;
|
||||
std::unique_ptr<FileLocation> photo_big;
|
||||
int32_t dc_id;
|
||||
|
||||
static UserProfilePhoto *TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error);
|
||||
};
|
||||
|
@ -293,7 +272,7 @@ public:
|
|||
class TL_userProfilePhoto : public UserProfilePhoto {
|
||||
|
||||
public:
|
||||
static const uint32_t constructor = 0xd559d8c8;
|
||||
static const uint32_t constructor = 0xecd75d8c;
|
||||
|
||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
||||
void serializeToStream(NativeByteBuffer *stream);
|
||||
|
@ -387,50 +366,6 @@ public:
|
|||
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 {
|
||||
|
||||
public:
|
||||
|
@ -439,173 +374,4 @@ public:
|
|||
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
|
||||
|
|
|
@ -111,7 +111,7 @@ void BuffersStorage::reuseFreeBuffer(NativeByteBuffer *buffer) {
|
|||
if (arrayToReuse->size() < maxCount) {
|
||||
arrayToReuse->push_back(buffer);
|
||||
} else {
|
||||
if (LOGS_ENABLED) DEBUG_D("too more %d buffers", capacity);
|
||||
if (LOGS_ENABLED) DEBUG_D("too much %d buffers", capacity);
|
||||
delete buffer;
|
||||
}
|
||||
if (isThreadSafe) {
|
||||
|
|
|
@ -288,6 +288,7 @@ void Connection::connect() {
|
|||
if (connectionState == TcpConnectionStageConnected || connectionState == TcpConnectionStageConnecting) {
|
||||
return;
|
||||
}
|
||||
connectionInProcess = true;
|
||||
connectionState = TcpConnectionStageConnecting;
|
||||
isMediaConnection = false;
|
||||
uint32_t ipv6 = ConnectionsManager::getInstance(currentDatacenter->instanceNum).isIpv6Enabled() ? TcpAddressFlagIpv6 : 0;
|
||||
|
@ -369,6 +370,7 @@ void Connection::connect() {
|
|||
setTimeout(12);
|
||||
}
|
||||
}
|
||||
connectionInProcess = false;
|
||||
}
|
||||
|
||||
void Connection::reconnect() {
|
||||
|
@ -643,7 +645,7 @@ inline void Connection::encryptKeyWithSecret(uint8_t *bytes, uint8_t secretType)
|
|||
SHA256_Final(bytes, &sha256Ctx);
|
||||
}
|
||||
|
||||
void Connection::onDisconnected(int32_t reason, int32_t error) {
|
||||
void Connection::onDisconnectedInternal(int32_t reason, int32_t error) {
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (ConnectionsManager::getInstance(currentDatacenter->instanceNum).isNetworkAvailable()) {
|
||||
if (ConnectionsManager::getInstance(currentDatacenter->instanceNum).isNetworkAvailable() && connectionType != ConnectionTypeProxy) {
|
||||
isTryingNextPort = true;
|
||||
if (failedConnectionCount > willRetryConnectCount || switchToNextPort) {
|
||||
currentDatacenter->nextAddressOrPort(currentAddressFlags);
|
||||
|
@ -706,6 +708,16 @@ void Connection::onDisconnected(int32_t reason, int32_t error) {
|
|||
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() {
|
||||
connectionState = TcpConnectionStageConnected;
|
||||
connectionToken = lastConnectionToken++;
|
||||
|
|
|
@ -69,6 +69,7 @@ private:
|
|||
|
||||
inline void encryptKeyWithSecret(uint8_t *array, uint8_t secretType);
|
||||
inline std::string *getCurrentSecret(uint8_t secretType);
|
||||
void onDisconnectedInternal(int32_t reason, int32_t error);
|
||||
|
||||
ProtocolType currentProtocolType = ProtocolTypeEE;
|
||||
|
||||
|
@ -94,6 +95,7 @@ private:
|
|||
bool forceNextPort = false;
|
||||
bool isMediaConnection = false;
|
||||
bool waitForReconnectTimer = false;
|
||||
bool connectionInProcess = false;
|
||||
uint32_t lastReconnectTimeout = 100;
|
||||
int64_t usefullDataReceiveTime;
|
||||
uint32_t currentTimeout = 4;
|
||||
|
|
|
@ -51,7 +51,8 @@ void ConnectionSocket::openConnection(std::string address, uint16_t port, bool i
|
|||
isIpv6 = ipv6;
|
||||
currentAddress = address;
|
||||
currentPort = port;
|
||||
int epolFd = ConnectionsManager::getInstance(instanceNum).epolFd;
|
||||
waitingForHostResolve = "";
|
||||
adjustWriteOpAfterResolve = false;
|
||||
ConnectionsManager::getInstance(instanceNum).attachConnection(this);
|
||||
|
||||
memset(&socketAddress, 0, sizeof(sockaddr_in));
|
||||
|
@ -96,15 +97,11 @@ void ConnectionSocket::openConnection(std::string address, uint16_t port, bool i
|
|||
continueCheckAddress = false;
|
||||
}
|
||||
if (continueCheckAddress) {
|
||||
std::string host = ConnectionsManager::getInstance(instanceNum).delegate->getHostByName(*proxyAddress, instanceNum);
|
||||
if (host.empty() || inet_pton(AF_INET, host.c_str(), &socketAddress.sin_addr.s_addr) != 1) {
|
||||
continueCheckAddress = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("connection(%p) can't resolve host %s address via delegate", this, proxyAddress->c_str());
|
||||
} else {
|
||||
continueCheckAddress = false;
|
||||
if (LOGS_ENABLED) DEBUG_D("connection(%p) resolved host %s address %x via delegate", this, proxyAddress->c_str(), socketAddress.sin_addr.s_addr);
|
||||
}
|
||||
if (continueCheckAddress) {
|
||||
#ifdef USE_DELEGATE_HOST_RESOLVE
|
||||
waitingForHostResolve = *proxyAddress;
|
||||
ConnectionsManager::getInstance(instanceNum).delegate->getHostByName(*proxyAddress, instanceNum, this);
|
||||
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());
|
||||
|
@ -121,7 +118,7 @@ void ConnectionSocket::openConnection(std::string address, uint16_t port, bool i
|
|||
closeSocket(1, -1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} 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;
|
||||
if (setsockopt(socketFd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(int))) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (adjustWriteOpAfterResolve) {
|
||||
adjustWriteOp();
|
||||
}
|
||||
}
|
||||
|
||||
int32_t ConnectionSocket::checkSocketError(int32_t *error) {
|
||||
|
@ -198,6 +203,8 @@ void ConnectionSocket::closeSocket(int32_t reason, int32_t error) {
|
|||
}
|
||||
socketFd = -1;
|
||||
}
|
||||
waitingForHostResolve = "";
|
||||
adjustWriteOpAfterResolve = false;
|
||||
proxyAuthState = 0;
|
||||
onConnectedSent = false;
|
||||
outgoingByteStream->clean();
|
||||
|
@ -401,6 +408,10 @@ void ConnectionSocket::writeBuffer(NativeByteBuffer *buffer) {
|
|||
}
|
||||
|
||||
void ConnectionSocket::adjustWriteOp() {
|
||||
if (!waitingForHostResolve.empty()) {
|
||||
adjustWriteOpAfterResolve = true;
|
||||
return;
|
||||
}
|
||||
eventMask.events = EPOLLIN | EPOLLRDHUP | EPOLLERR | EPOLLET;
|
||||
if (proxyAuthState == 0 && (outgoingByteStream->hasData() || !onConnectedSent) || proxyAuthState == 1 || proxyAuthState == 3 || proxyAuthState == 5) {
|
||||
eventMask.events |= EPOLLOUT;
|
||||
|
@ -433,6 +444,10 @@ void ConnectionSocket::checkTimeout(int64_t now) {
|
|||
}
|
||||
}
|
||||
|
||||
void ConnectionSocket::resetLastEventTime() {
|
||||
lastEventTime = ConnectionsManager::getInstance(instanceNum).getCurrentTimeMonotonicMillis();
|
||||
}
|
||||
|
||||
bool ConnectionSocket::isDisconnected() {
|
||||
return socketFd < 0;
|
||||
}
|
||||
|
@ -448,3 +463,18 @@ void ConnectionSocket::setOverrideProxy(std::string address, uint16_t port, std:
|
|||
overrideProxyPassword = password;
|
||||
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();
|
||||
void dropConnection();
|
||||
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:
|
||||
int32_t instanceNum;
|
||||
void onEvent(uint32_t events);
|
||||
void checkTimeout(int64_t now);
|
||||
void resetLastEventTime();
|
||||
virtual void onReceivedData(NativeByteBuffer *buffer) = 0;
|
||||
virtual void onDisconnected(int32_t reason, int32_t error) = 0;
|
||||
virtual void onConnected() = 0;
|
||||
|
@ -63,12 +65,16 @@ private:
|
|||
std::string currentAddress;
|
||||
uint16_t currentPort;
|
||||
|
||||
std::string waitingForHostResolve;
|
||||
bool adjustWriteOpAfterResolve;
|
||||
|
||||
uint8_t buffer[1024];
|
||||
|
||||
uint8_t proxyAuthState;
|
||||
|
||||
int32_t checkSocketError(int32_t *error);
|
||||
void closeSocket(int32_t reason, int32_t error);
|
||||
void openConnectionInternal(bool ipv6);
|
||||
void adjustWriteOp();
|
||||
|
||||
friend class EventObject;
|
||||
|
|
|
@ -159,7 +159,7 @@ int ConnectionsManager::callEvents(int64_t now) {
|
|||
if (!networkPaused) {
|
||||
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) {
|
||||
return 1000;
|
||||
}
|
||||
|
@ -168,13 +168,19 @@ int ConnectionsManager::callEvents(int64_t now) {
|
|||
}
|
||||
|
||||
void ConnectionsManager::checkPendingTasks() {
|
||||
int32_t count = INT_MAX;
|
||||
while (true) {
|
||||
std::function<void()> task;
|
||||
pthread_mutex_lock(&mutex);
|
||||
if (pendingTasks.empty()) {
|
||||
if (pendingTasks.empty() || count <= 0) {
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return;
|
||||
}
|
||||
if (count == INT_MAX) {
|
||||
count = (int32_t) pendingTasks.size();
|
||||
} else {
|
||||
count--;
|
||||
}
|
||||
task = pendingTasks.front();
|
||||
pendingTasks.pop();
|
||||
pthread_mutex_unlock(&mutex);
|
||||
|
@ -199,7 +205,7 @@ void ConnectionsManager::select() {
|
|||
|
||||
Datacenter *datacenter = getDatacenterWithId(currentDatacenterId);
|
||||
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;
|
||||
sendingPushPing = false;
|
||||
if (datacenter != nullptr) {
|
||||
|
@ -210,9 +216,12 @@ void ConnectionsManager::select() {
|
|||
}
|
||||
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");
|
||||
lastPushPingTime = now;
|
||||
uint8_t offset;
|
||||
RAND_bytes(&offset, 1);
|
||||
nextPingTimeOffset = 60000 * 3 + (offset % 40) - 20;
|
||||
if (datacenter != nullptr) {
|
||||
sendPing(datacenter, true);
|
||||
}
|
||||
|
@ -669,9 +678,8 @@ void ConnectionsManager::onConnectionClosed(Connection *connection, int reason)
|
|||
} else if (connection->getConnectionType() == ConnectionTypePush) {
|
||||
if (LOGS_ENABLED) DEBUG_D("connection(%p) push connection closed", connection);
|
||||
sendingPushPing = false;
|
||||
lastPushPingTime = getCurrentTimeMonotonicMillis() - 60000 * 3 + 4000;
|
||||
lastPushPingTime = getCurrentTimeMonotonicMillis() - nextPingTimeOffset + 4000;
|
||||
} else if (connection->getConnectionType() == ConnectionTypeProxy) {
|
||||
scheduleTask([&, connection] {
|
||||
for (std::vector<std::unique_ptr<ProxyCheckInfo>>::iterator iter = proxyActiveChecks.begin(); iter != proxyActiveChecks.end(); iter++) {
|
||||
ProxyCheckInfo *proxyCheckInfo = iter->get();
|
||||
if (proxyCheckInfo->connectionNum == connection->getConnectionNum()) {
|
||||
|
@ -697,7 +705,6 @@ void ConnectionsManager::onConnectionClosed(Connection *connection, int reason)
|
|||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2365,7 +2372,7 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t
|
|||
} else {
|
||||
currentCount = 0;
|
||||
}
|
||||
if (!networkAvailable || currentCount >= 6) {
|
||||
if (!networkAvailable || currentCount >= 10) {
|
||||
iter++;
|
||||
continue;
|
||||
}
|
||||
|
@ -2427,6 +2434,9 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t
|
|||
networkMessage->invokeAfter = (request->requestFlags & RequestFlagInvokeAfter) != 0;
|
||||
networkMessage->needQuickAck = (request->requestFlags & RequestFlagNeedQuickAck) != 0;
|
||||
|
||||
if (!hasPendingRequestsForConnection(connection)) {
|
||||
connection->resetLastEventTime();
|
||||
}
|
||||
runningRequests.push_back(std::move(*iter));
|
||||
|
||||
switch (request->connectionType & 0x0000ffff) {
|
||||
|
@ -3202,7 +3212,7 @@ void ConnectionsManager::checkProxyInternal(ProxyCheckInfo *proxyCheckInfo) {
|
|||
} else {
|
||||
ConnectionType connectionType = (ConnectionType) (ConnectionTypeProxy | (freeConnectionNum << 16));
|
||||
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) {
|
||||
connection->setOverrideProxy(proxyCheckInfo->address, proxyCheckInfo->port, proxyCheckInfo->username, proxyCheckInfo->password, proxyCheckInfo->secret);
|
||||
connection->suspendConnection();
|
||||
|
|
|
@ -146,6 +146,7 @@ private:
|
|||
int32_t currentPingTime = 0;
|
||||
bool registeringForPush = false;
|
||||
int64_t lastPushPingTime = 0;
|
||||
int32_t nextPingTimeOffset = 60000 * 3;
|
||||
bool sendingPushPing = false;
|
||||
bool sendingPing = false;
|
||||
bool updatingDcSettings = false;
|
||||
|
@ -233,7 +234,6 @@ private:
|
|||
friend class TL_message;
|
||||
friend class TL_rpc_result;
|
||||
friend class Config;
|
||||
friend class FileLoadOperation;
|
||||
friend class FileLog;
|
||||
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);
|
||||
if (authKey == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
if (create) {
|
||||
createProxyConnection(num)->connect();
|
||||
Connection *connection = createProxyConnection(num);
|
||||
if (connect) {
|
||||
connection->connect();
|
||||
}
|
||||
}
|
||||
return proxyConnection[num];
|
||||
}
|
||||
|
@ -1349,7 +1352,7 @@ Connection *Datacenter::getConnectionByType(uint32_t connectionType, bool create
|
|||
case ConnectionTypeTemp:
|
||||
return getTempConnection(create);
|
||||
case ConnectionTypeProxy:
|
||||
return getProxyConnection(connectionNum, create);
|
||||
return getProxyConnection(connectionNum, create, create);
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ public:
|
|||
void resetInitVersion();
|
||||
|
||||
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 *getGenericConnection(bool create, int32_t allowPendingKey);
|
||||
Connection *getGenericMediaConnection(bool create, int32_t allowPendingKey);
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#define UPLOAD_CONNECTIONS_COUNT 4
|
||||
#define CONNECTION_BACKGROUND_KEEP_TIME 10000
|
||||
#define MAX_ACCOUNT_COUNT 3
|
||||
#define USE_DELEGATE_HOST_RESOLVE
|
||||
|
||||
#define DOWNLOAD_CHUNK_SIZE 1024 * 32
|
||||
#define DOWNLOAD_CHUNK_BIG_SIZE 1024 * 128
|
||||
|
@ -47,8 +48,8 @@ class Request;
|
|||
class TL_message;
|
||||
class TL_config;
|
||||
class NativeByteBuffer;
|
||||
class FileLoadOperation;
|
||||
class Handshake;
|
||||
class ConnectionSocket;
|
||||
|
||||
typedef std::function<void(TLObject *response, TL_error *error, int32_t networkType)> onCompleteFunc;
|
||||
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 onRequestNewServerIpAndPort(int32_t second, 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;
|
||||
} 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 *;
|
||||
}
|
||||
-keep class org.telegram.** { *; }
|
||||
#-keep class com.google.android.exoplayer2.** { *; }
|
||||
-keep class com.google.android.exoplayer2.ext.** { *; }
|
||||
-keep class com.google.android.exoplayer2.util.** { *; }
|
||||
-keep class com.coremedia.** { *; }
|
||||
-keep class com.googlecode.mp4parser.** { *; }
|
||||
-dontwarn com.coremedia.**
|
||||
-dontwarn org.telegram.**
|
||||
-dontwarn com.google.android.exoplayer2.ext.**
|
||||
|
|
|
@ -70,8 +70,8 @@
|
|||
android:name="org.telegram.messenger.${applicationClassName}"
|
||||
android:allowBackup="false"
|
||||
android:hardwareAccelerated="@bool/useHardwareAcceleration"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:roundIcon="@drawable/ic_launcher"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:largeHeap="true"
|
||||
android:theme="@style/Theme.TMessages.Start"
|
||||
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="https" />
|
||||
</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" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
@ -151,7 +151,7 @@
|
|||
android:excludeFromRecents="true"
|
||||
android:stateNotNeeded="true"
|
||||
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" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
@ -163,7 +163,7 @@
|
|||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||
android:hardwareAccelerated="@bool/useHardwareAcceleration"
|
||||
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"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
|
@ -349,7 +349,7 @@
|
|||
<receiver android:name=".voip.VoIPActionsReceiver" android:exported="false"/>
|
||||
|
||||
<provider
|
||||
android:name="android.support.v4.content.FileProvider"
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
|
@ -369,6 +369,8 @@
|
|||
android:name=".voip.CallNotificationSoundProvider"
|
||||
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" />
|
||||
<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" />
|
||||
|
@ -383,7 +385,6 @@
|
|||
|
||||
<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" />
|
||||
|
||||
</application>
|
||||
|
|
|
@ -30,8 +30,10 @@ emptyListPlaceholder=-11247768
|
|||
chat_inAudioSelectedProgress=-14925469
|
||||
chats_nameMessage=-8932123
|
||||
chat_inMediaIcon=-1
|
||||
actionBarDefaultArchived=-13684171
|
||||
avatar_subtitleInProfileViolet=-7697782
|
||||
chat_messagePanelCancelInlineBot=-6971219
|
||||
dialogSearchBackground=-13946829
|
||||
chat_outAudioSeekbarFill=-8272902
|
||||
chat_botKeyboardButtonBackgroundPressed=-13617349
|
||||
player_time=-8550517
|
||||
|
@ -41,6 +43,7 @@ chat_inFileProgressSelected=-5845010
|
|||
changephoneinfo_image=-10786960
|
||||
chat_inAudioPerfomerText=-8351328
|
||||
player_button=-1
|
||||
key_sheet_other=754974719
|
||||
chat_inContactNameText=-1
|
||||
switch2Track=-2135965
|
||||
chats_menuPhoneCats=-8287602
|
||||
|
@ -53,12 +56,14 @@ chat_outVoiceSeekbar=-1300913456
|
|||
player_actionBarTitle=-1
|
||||
dialogGrayLine=-13354948
|
||||
chat_outFileIcon=-13143396
|
||||
chats_nameMessage_threeLines=-1644826
|
||||
chat_inFileProgress=-10653824
|
||||
dialogIcon=-8747891
|
||||
chat_emojiPanelEmptyText=-11511454
|
||||
chat_emojiPanelBackspace=-10458511
|
||||
chat_inContactPhoneSelectedText=-8085320
|
||||
chat_replyPanelClose=-6905432
|
||||
dialogSearchText=-723724
|
||||
chat_outAudioTitleText=-1
|
||||
chat_emojiPanelBackground=-14999775
|
||||
chats_unreadCounter=-11032854
|
||||
|
@ -97,6 +102,7 @@ chat_outFileSelectedIcon=-13925429
|
|||
picker_disabledButton=-10524558
|
||||
groupcreate_spanBackground=-14143949
|
||||
dialogButton=-10964761
|
||||
contextProgressInner1=800774134
|
||||
chat_inLoaderPhotoIconSelected=-5648402
|
||||
actionBarDefaultSubtitle=-8156785
|
||||
chat_inContactPhoneText=-8812393
|
||||
|
@ -147,11 +153,13 @@ avatar_nameInMessageRed=-1084559
|
|||
chat_outLoaderPhoto=-13077852
|
||||
chat_botSwitchToInlineText=-9456666
|
||||
dialogTextRed2=-892058
|
||||
chats_nameMessageArchived=-9011322
|
||||
avatar_nameInMessageOrange=-1265812
|
||||
chats_pinnedIcon=-10524305
|
||||
chat_replyPanelLine=1907997
|
||||
avatar_subtitleInProfileOrange=-7697782
|
||||
chat_outSentCheckSelected=-7156228
|
||||
dialogSearchHint=-8288371
|
||||
chat_inVenueInfoSelectedText=-8085320
|
||||
dialogTextBlue2=-9456666
|
||||
avatar_backgroundGroupCreateSpanBlue=-12751207
|
||||
|
@ -180,7 +188,7 @@ avatar_nameInMessageCyan=-8270884
|
|||
chat_mediaLoaderPhotoIconSelected=-1842205
|
||||
chat_inLocationBackground=-13419450
|
||||
radioBackground=-10524811
|
||||
contextProgressOuter1=-11358225
|
||||
contextProgressOuter1=-10702088
|
||||
chat_inFileIcon=-13946055
|
||||
avatar_backgroundActionBarPink=-14605274
|
||||
dialogTextGray3=-7892583
|
||||
|
@ -208,6 +216,7 @@ chat_outForwardedNameText=-5515009
|
|||
dialogRoundCheckBox=-9912583
|
||||
chat_emojiPanelTrendingTitle=-2167820
|
||||
switchThumbChecked=-13600600
|
||||
chats_nameMessageArchived_threeLines=-1644826
|
||||
chat_outSiteNameText=-5515009
|
||||
windowBackgroundWhite=-14737118
|
||||
chat_inVoiceSeekbarSelected=-2057139272
|
||||
|
@ -271,12 +280,12 @@ windowBackgroundWhiteGrayText=-9603715
|
|||
musicPicker_buttonBackground=-11035162
|
||||
avatar_actionBarSelectorViolet=-11972268
|
||||
avatar_nameInMessageBlue=-9456666
|
||||
dialogTextBlack=-1
|
||||
dialogTextBlack=-328966
|
||||
actionBarDefault=-14276309
|
||||
profile_actionIcon=-1
|
||||
windowBackgroundUnchecked=-14473945
|
||||
actionBarDefaultSelector=-11972268
|
||||
chats_menuTopShadow=-15724528
|
||||
chats_menuTopShadow=-1558504677
|
||||
chat_outAudioPerfomerText=-6965025
|
||||
sharedMedia_startStopLoadIcon=-11164432
|
||||
chat_serviceBackgroundSelected=1614498132
|
||||
|
@ -287,7 +296,7 @@ chat_outSentClockSelected=-6764038
|
|||
switchTrackBlueSelectorChecked=848091135
|
||||
musicPicker_checkbox=-11621658
|
||||
chat_outFileBackground=-11829594
|
||||
chats_name=-1
|
||||
chats_name=-657931
|
||||
chat_attachSendBackground=-10242065
|
||||
switchTrackBlueSelector=431611386
|
||||
dialogBadgeBackground=-10371847
|
||||
|
@ -338,6 +347,7 @@ chat_messagePanelVoiceDelete=-9997440
|
|||
chat_inAudioProgress=-1
|
||||
chats_date=-9011322
|
||||
chat_messagePanelText=-1
|
||||
key_sheet_scrollUp=603979775
|
||||
player_buttonActive=-9456666
|
||||
chat_outLoaderPhotoIcon=-9263664
|
||||
chat_outContactBackground=-9194520
|
||||
|
@ -360,10 +370,13 @@ chats_sentClock=-8740661
|
|||
chat_inAudioSeekbar=-581869200
|
||||
avatar_subtitleInProfileRed=-7697782
|
||||
avatar_backgroundActionBarRed=-14605274
|
||||
dialogSearchIcon=-8814973
|
||||
chat_inPreviewInstantText=-9456666
|
||||
chats_archiveBackground=-10642482
|
||||
chat_inViews=-8812137
|
||||
chat_outLoaderSelected=-9194520
|
||||
dialogButtonSelector=352321535
|
||||
chats_archivePinBackground=-13749706
|
||||
player_actionBarItems=-1
|
||||
chat_sentError=-1551526
|
||||
player_progressBackground=-13288897
|
||||
|
|
|
@ -22,7 +22,7 @@ switchTrack=-10984850
|
|||
chat_inPreviewInstantSelectedText=-5648402
|
||||
chat_attachAudioBackground=-619421
|
||||
location_sendLocationBackground=-9919529
|
||||
actionBarDefaultSubmenuBackground=-13878451
|
||||
actionBarDefaultSubmenuBackground=-14075831
|
||||
switchTrackBlueThumb=-14866637
|
||||
avatar_nameInMessageViolet=-6643205
|
||||
emptyListPlaceholder=-8549479
|
||||
|
@ -30,8 +30,10 @@ chat_inAudioSelectedProgress=-1
|
|||
chats_nameMessage=-1446156
|
||||
chat_messagePanelShadow=2030043136
|
||||
chat_inMediaIcon=-1
|
||||
actionBarDefaultArchived=-13748149
|
||||
avatar_subtitleInProfileViolet=-7628894
|
||||
chat_messagePanelCancelInlineBot=-8549479
|
||||
dialogSearchBackground=-14010037
|
||||
chat_outAudioSeekbarFill=-7944965
|
||||
chat_botKeyboardButtonBackgroundPressed=-12956574
|
||||
player_time=-8549479
|
||||
|
@ -41,6 +43,7 @@ chat_inFileProgressSelected=-1
|
|||
changephoneinfo_image=-12693922
|
||||
chat_inAudioPerfomerText=-8812393
|
||||
player_button=-1
|
||||
key_sheet_other=1140850687
|
||||
chat_inContactNameText=-8796932
|
||||
chats_menuPhoneCats=-11049613
|
||||
chat_outPreviewLine=-6631937
|
||||
|
@ -51,12 +54,14 @@ chat_outVoiceSeekbar=-429551165
|
|||
player_actionBarTitle=-1
|
||||
dialogGrayLine=-15790062
|
||||
chat_outFileIcon=-12689015
|
||||
chats_nameMessage_threeLines=-1446156
|
||||
chat_inFileProgress=-1
|
||||
dialogIcon=-8944238
|
||||
dialogIcon=-7627862
|
||||
chat_emojiPanelEmptyText=-8549479
|
||||
chat_emojiPanelBackspace=-9996665
|
||||
chat_replyPanelClose=-10062202
|
||||
chat_inContactPhoneSelectedText=-7490861
|
||||
dialogSearchText=-1
|
||||
chat_outAudioTitleText=-1
|
||||
chat_emojiPanelBackground=-14866637
|
||||
chats_unreadCounter=-10177041
|
||||
|
@ -92,6 +97,7 @@ chat_outFileSelectedIcon=-13925429
|
|||
picker_disabledButton=-11047552
|
||||
groupcreate_spanBackground=-13878194
|
||||
dialogButton=-10177041
|
||||
contextProgressInner1=-11509903
|
||||
chat_inLoaderPhotoIconSelected=-1
|
||||
actionBarDefaultSubtitle=-7628894
|
||||
chat_inContactPhoneText=-8812393
|
||||
|
@ -142,11 +148,13 @@ avatar_nameInMessageRed=-21124
|
|||
chat_outLoaderPhoto=-12623479
|
||||
chat_botSwitchToInlineText=-8796932
|
||||
dialogTextRed2=-1152913
|
||||
chats_nameMessageArchived=-8549479
|
||||
avatar_nameInMessageOrange=-13984
|
||||
chats_pinnedIcon=-10982016
|
||||
chat_replyPanelLine=1779898909
|
||||
avatar_subtitleInProfileOrange=-7628894
|
||||
chat_outSentCheckSelected=-4268038
|
||||
dialogSearchHint=-8419182
|
||||
chat_inVenueInfoSelectedText=-7490861
|
||||
dialogTextBlue2=-10177041
|
||||
avatar_backgroundGroupCreateSpanBlue=-13803892
|
||||
|
@ -173,7 +181,7 @@ windowBackgroundWhiteBlueText=-10177041
|
|||
avatar_nameInMessageCyan=-10623523
|
||||
chat_inLocationBackground=-13417903
|
||||
radioBackground=-1635939431
|
||||
contextProgressOuter1=-10177041
|
||||
contextProgressOuter1=-9914632
|
||||
chat_inFileIcon=-14470078
|
||||
avatar_backgroundActionBarPink=-14602949
|
||||
dialogTextGray3=-8549479
|
||||
|
@ -195,22 +203,25 @@ chat_inBubbleSelected=-13546911
|
|||
chat_mediaMenu=-1
|
||||
chat_outViewsSelected=-4268038
|
||||
chat_outInstant=-7551233
|
||||
chat_emojiPanelShadowLine=251658239
|
||||
actionBarActionModeDefaultSelector=2047809827
|
||||
chat_emojiPanelShadowLine=838860800
|
||||
actionBarActionModeDefaultSelector=2050907520
|
||||
chat_outForwardedNameText=-7551233
|
||||
dialogRoundCheckBox=-10177041
|
||||
chat_emojiPanelTrendingTitle=-1
|
||||
switchThumbChecked=-10376479
|
||||
chat_stickersHintPanel=-13484721
|
||||
chat_outSiteNameText=-1
|
||||
windowBackgroundWhite=-14866637
|
||||
groupcreate_offlineText=-8549479
|
||||
chat_inVoiceSeekbarSelected=-9203285
|
||||
dialogTextGray=-8549479
|
||||
chat_messageLinkOut=-6631937
|
||||
avatar_backgroundArchived=-13087910
|
||||
chat_outFileInfoSelectedText=-4268038
|
||||
chats_tabletSelectedOverlay=268435455
|
||||
chat_outAudioDurationSelectedText=-4268038
|
||||
chat_attachCameraIcon1=-32171
|
||||
undo_background=-182112197
|
||||
avatar_actionBarSelectorPink=-12758164
|
||||
dialogTextHint=-8549479
|
||||
chat_topPanelTitle=-11164709
|
||||
|
@ -226,7 +237,7 @@ chats_sentCheck=-10177041
|
|||
chats_unreadCounterMuted=-12692893
|
||||
chat_outVoiceSeekbarFill=-7944965
|
||||
chat_outReplyLine=-6631937
|
||||
chat_messagePanelIcons=-9996665
|
||||
chat_messagePanelIcons=-9733492
|
||||
chat_inReplyMediaMessageText=-8812393
|
||||
inappPlayerTitle=-8549479
|
||||
chat_emojiPanelIconSelected=-10177041
|
||||
|
@ -259,7 +270,7 @@ avatar_nameInMessagePink=-624741
|
|||
windowBackgroundWhiteGrayText=-8549479
|
||||
avatar_actionBarSelectorViolet=-12758164
|
||||
avatar_nameInMessageBlue=-8796932
|
||||
dialogTextBlack=-1
|
||||
dialogTextBlack=-592138
|
||||
actionBarDefault=-14602949
|
||||
location_placeLocationBackground=-9919529
|
||||
profile_actionIcon=-1
|
||||
|
@ -330,6 +341,7 @@ chat_messagePanelVoiceDelete=-1
|
|||
chat_inAudioProgress=-1
|
||||
chats_date=-9207925
|
||||
chat_messagePanelText=-1
|
||||
key_sheet_scrollUp=637534207
|
||||
player_buttonActive=-10177041
|
||||
chat_outLoaderPhotoIcon=-1
|
||||
chat_outContactBackground=-9919529
|
||||
|
@ -345,17 +357,21 @@ windowBackgroundWhiteLinkSelection=862238205
|
|||
player_background=-14734794
|
||||
inappPlayerClose=-8549479
|
||||
chat_outMediaIcon=-1
|
||||
chats_message_threeLines=-8549479
|
||||
player_actionBarSubtitle=-8549479
|
||||
chat_outAudioCacheSeekbar=-10120765
|
||||
chats_sentClock=-11772054
|
||||
chat_inAudioSeekbar=-11443856
|
||||
avatar_subtitleInProfileRed=-7628894
|
||||
avatar_backgroundActionBarRed=-14602949
|
||||
dialogSearchIcon=-8945521
|
||||
chat_inPreviewInstantText=-8796932
|
||||
chats_archiveBackground=-11036980
|
||||
dialog_liveLocationProgress=-9919529
|
||||
chat_inViews=-8812137
|
||||
chat_outLoaderSelected=-9919529
|
||||
dialogButtonSelector=352321535
|
||||
chats_archivePinBackground=-13746613
|
||||
player_actionBarItems=-1
|
||||
chat_sentError=-633010
|
||||
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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -14,11 +14,12 @@
|
|||
* 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 androidx.core.util.Pools;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
|
@ -14,10 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.telegram.messenger.support.util;
|
||||
package androidx.recyclerview.widget;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import org.telegram.messenger.support.widget.RecyclerView;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* 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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -14,14 +14,17 @@
|
|||
* 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.SparseBooleanArray;
|
||||
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.
|
||||
* <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
|
||||
* secondary storage such as disk, but not from network.
|
||||
* <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.
|
||||
*
|
||||
*/
|
||||
|
@ -84,8 +87,8 @@ public class AsyncListUtil<T> {
|
|||
* @param dataCallback Data access callback.
|
||||
* @param viewCallback Callback for querying visible item range and update notifications.
|
||||
*/
|
||||
public AsyncListUtil(Class<T> klass, int tileSize, DataCallback<T> dataCallback,
|
||||
ViewCallback viewCallback) {
|
||||
public AsyncListUtil(@NonNull Class<T> klass, int tileSize,
|
||||
@NonNull DataCallback<T> dataCallback, @NonNull ViewCallback viewCallback) {
|
||||
mTClass = klass;
|
||||
mTileSize = tileSize;
|
||||
mDataCallback = dataCallback;
|
||||
|
@ -110,7 +113,7 @@ public class AsyncListUtil<T> {
|
|||
* <p>
|
||||
* 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
|
||||
* {@link android.support.v7.widget.RecyclerView.OnScrollListener#onScrolled}).
|
||||
* {@link RecyclerView.OnScrollListener#onScrolled}).
|
||||
*/
|
||||
public void onRangeChanged() {
|
||||
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
|
||||
* yet.
|
||||
*/
|
||||
@Nullable
|
||||
public T getItem(int position) {
|
||||
if (position < 0 || position >= mItemCount) {
|
||||
throw new IndexOutOfBoundsException(position + " is not within 0 and " + mItemCount);
|
||||
|
@ -471,7 +475,7 @@ public class AsyncListUtil<T> {
|
|||
* <code>itemCount</code>.
|
||||
*/
|
||||
@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.
|
||||
|
@ -481,7 +485,7 @@ public class AsyncListUtil<T> {
|
|||
* @param itemCount The data item count.
|
||||
*/
|
||||
@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.
|
||||
*/
|
||||
@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.
|
||||
|
@ -569,7 +573,7 @@ public class AsyncListUtil<T> {
|
|||
* @param scrollHint The scroll direction hint.
|
||||
*/
|
||||
@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 halfRange = fullRange / 2;
|
||||
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");
|
||||
* 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
|
||||
* 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.
|
||||
|
@ -22,7 +24,7 @@ package org.telegram.messenger.support.util;
|
|||
* BatchingListUpdateCallback merges them and calls the wrapped callback only once.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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;
|
||||
Object mLastEventPayload = null;
|
||||
|
||||
public BatchingListUpdateCallback(ListUpdateCallback callback) {
|
||||
public BatchingListUpdateCallback(@NonNull ListUpdateCallback 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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.telegram.messenger.support.widget;
|
||||
package androidx.recyclerview.widget;
|
||||
|
||||
import android.util.Log;
|
||||
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");
|
||||
* 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
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.support.widget;
|
||||
package androidx.recyclerview.widget;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.TimeInterpolator;
|
||||
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.ViewPropertyAnimator;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.view.ViewCompat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -40,27 +40,27 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
|||
|
||||
private static TimeInterpolator sDefaultInterpolator;
|
||||
|
||||
private ArrayList<ViewHolder> mPendingRemovals = new ArrayList<>();
|
||||
private ArrayList<ViewHolder> mPendingAdditions = new ArrayList<>();
|
||||
private ArrayList<RecyclerView.ViewHolder> mPendingRemovals = new ArrayList<>();
|
||||
private ArrayList<RecyclerView.ViewHolder> mPendingAdditions = new ArrayList<>();
|
||||
private ArrayList<MoveInfo> mPendingMoves = 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<ChangeInfo>> mChangesList = new ArrayList<>();
|
||||
|
||||
ArrayList<ViewHolder> mAddAnimations = new ArrayList<>();
|
||||
ArrayList<ViewHolder> mMoveAnimations = new ArrayList<>();
|
||||
ArrayList<ViewHolder> mRemoveAnimations = new ArrayList<>();
|
||||
ArrayList<ViewHolder> mChangeAnimations = new ArrayList<>();
|
||||
ArrayList<RecyclerView.ViewHolder> mAddAnimations = new ArrayList<>();
|
||||
ArrayList<RecyclerView.ViewHolder> mMoveAnimations = new ArrayList<>();
|
||||
ArrayList<RecyclerView.ViewHolder> mRemoveAnimations = new ArrayList<>();
|
||||
ArrayList<RecyclerView.ViewHolder> mChangeAnimations = new ArrayList<>();
|
||||
|
||||
private boolean delayAnimations = true;
|
||||
|
||||
private static class MoveInfo {
|
||||
public ViewHolder holder;
|
||||
public RecyclerView.ViewHolder holder;
|
||||
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.fromX = fromX;
|
||||
this.fromY = fromY;
|
||||
|
@ -70,14 +70,14 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
|||
}
|
||||
|
||||
private static class ChangeInfo {
|
||||
public ViewHolder oldHolder, newHolder;
|
||||
public RecyclerView.ViewHolder oldHolder, newHolder;
|
||||
public int fromX, fromY, toX, toY;
|
||||
private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder) {
|
||||
private ChangeInfo(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder) {
|
||||
this.oldHolder = oldHolder;
|
||||
this.newHolder = newHolder;
|
||||
}
|
||||
|
||||
ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder,
|
||||
ChangeInfo(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder,
|
||||
int fromX, int fromY, int toX, int toY) {
|
||||
this(oldHolder, newHolder);
|
||||
this.fromX = fromX;
|
||||
|
@ -110,13 +110,14 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
|||
return;
|
||||
}
|
||||
// First, remove stuff
|
||||
for (ViewHolder holder : mPendingRemovals) {
|
||||
for (RecyclerView.ViewHolder holder : mPendingRemovals) {
|
||||
animateRemoveImpl(holder);
|
||||
}
|
||||
mPendingRemovals.clear();
|
||||
// Next, move stuff
|
||||
if (movesPending) {
|
||||
final ArrayList<MoveInfo> moves = new ArrayList<>(mPendingMoves);
|
||||
final ArrayList<MoveInfo> moves = new ArrayList<>();
|
||||
moves.addAll(mPendingMoves);
|
||||
mMovesList.add(moves);
|
||||
mPendingMoves.clear();
|
||||
Runnable mover = new Runnable() {
|
||||
|
@ -139,7 +140,8 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
|||
}
|
||||
// Next, change stuff, to run in parallel with move animations
|
||||
if (changesPending) {
|
||||
final ArrayList<ChangeInfo> changes = new ArrayList<>(mPendingChanges);
|
||||
final ArrayList<ChangeInfo> changes = new ArrayList<>();
|
||||
changes.addAll(mPendingChanges);
|
||||
mChangesList.add(changes);
|
||||
mPendingChanges.clear();
|
||||
Runnable changer = new Runnable() {
|
||||
|
@ -153,7 +155,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
|||
}
|
||||
};
|
||||
if (delayAnimations && removalsPending) {
|
||||
ViewHolder holder = changes.get(0).oldHolder;
|
||||
RecyclerView.ViewHolder holder = changes.get(0).oldHolder;
|
||||
ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());
|
||||
} else {
|
||||
changer.run();
|
||||
|
@ -161,13 +163,14 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
|||
}
|
||||
// Next, add stuff
|
||||
if (additionsPending) {
|
||||
final ArrayList<ViewHolder> additions = new ArrayList<>(mPendingAdditions);
|
||||
final ArrayList<RecyclerView.ViewHolder> additions = new ArrayList<>();
|
||||
additions.addAll(mPendingAdditions);
|
||||
mAdditionsList.add(additions);
|
||||
mPendingAdditions.clear();
|
||||
Runnable adder = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (ViewHolder holder : additions) {
|
||||
for (RecyclerView.ViewHolder holder : additions) {
|
||||
animateAddImpl(holder);
|
||||
}
|
||||
additions.clear();
|
||||
|
@ -188,7 +191,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean animateRemove(final ViewHolder holder) {
|
||||
public boolean animateRemove(final RecyclerView.ViewHolder holder) {
|
||||
resetAnimation(holder);
|
||||
mPendingRemovals.add(holder);
|
||||
return true;
|
||||
|
@ -198,7 +201,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
|||
delayAnimations = value;
|
||||
}
|
||||
|
||||
private void animateRemoveImpl(final ViewHolder holder) {
|
||||
private void animateRemoveImpl(final RecyclerView.ViewHolder holder) {
|
||||
final View view = holder.itemView;
|
||||
final ViewPropertyAnimator animation = view.animate();
|
||||
mRemoveAnimations.add(holder);
|
||||
|
@ -221,14 +224,14 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean animateAdd(final ViewHolder holder) {
|
||||
public boolean animateAdd(final RecyclerView.ViewHolder holder) {
|
||||
resetAnimation(holder);
|
||||
holder.itemView.setAlpha(0);
|
||||
mPendingAdditions.add(holder);
|
||||
return true;
|
||||
}
|
||||
|
||||
void animateAddImpl(final ViewHolder holder) {
|
||||
void animateAddImpl(final RecyclerView.ViewHolder holder) {
|
||||
final View view = holder.itemView;
|
||||
final ViewPropertyAnimator animation = view.animate();
|
||||
mAddAnimations.add(holder);
|
||||
|
@ -255,7 +258,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
|||
}
|
||||
|
||||
@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) {
|
||||
final View view = holder.itemView;
|
||||
fromX += (int) holder.itemView.getTranslationX();
|
||||
|
@ -277,7 +280,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
|||
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 int deltaX = toX - fromX;
|
||||
final int deltaY = toY - fromY;
|
||||
|
@ -319,7 +322,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
|||
}
|
||||
|
||||
@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) {
|
||||
if (oldHolder == newHolder) {
|
||||
// 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) {
|
||||
final ViewHolder holder = changeInfo.oldHolder;
|
||||
final RecyclerView.ViewHolder holder = changeInfo.oldHolder;
|
||||
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;
|
||||
if (view != null) {
|
||||
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--) {
|
||||
ChangeInfo changeInfo = infoList.get(i);
|
||||
if (endChangeAnimationIfNecessary(changeInfo, item)) {
|
||||
|
@ -418,7 +421,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
|||
endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder);
|
||||
}
|
||||
}
|
||||
private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, ViewHolder item) {
|
||||
private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, RecyclerView.ViewHolder item) {
|
||||
boolean oldItem = false;
|
||||
if (changeInfo.newHolder == item) {
|
||||
changeInfo.newHolder = null;
|
||||
|
@ -436,7 +439,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void endAnimation(ViewHolder item) {
|
||||
public void endAnimation(RecyclerView.ViewHolder item) {
|
||||
final View view = item.itemView;
|
||||
// this will trigger end callback which should set properties to their target values.
|
||||
view.animate().cancel();
|
||||
|
@ -484,7 +487,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
|||
}
|
||||
}
|
||||
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)) {
|
||||
view.setAlpha(1);
|
||||
dispatchAddFinished(item);
|
||||
|
@ -521,7 +524,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
|||
dispatchFinishedWhenDone();
|
||||
}
|
||||
|
||||
private void resetAnimation(ViewHolder holder) {
|
||||
private void resetAnimation(RecyclerView.ViewHolder holder) {
|
||||
if (sDefaultInterpolator == null) {
|
||||
sDefaultInterpolator = new ValueAnimator().getInterpolator();
|
||||
}
|
||||
|
@ -552,9 +555,14 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
|||
void dispatchFinishedWhenDone() {
|
||||
if (!isRunning()) {
|
||||
dispatchAnimationsFinished();
|
||||
onAllAnimationsDone();
|
||||
}
|
||||
}
|
||||
|
||||
protected void onAllAnimationsDone() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endAnimations() {
|
||||
int count = mPendingMoves.size();
|
||||
|
@ -568,13 +576,13 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
|||
}
|
||||
count = mPendingRemovals.size();
|
||||
for (int i = count - 1; i >= 0; i--) {
|
||||
ViewHolder item = mPendingRemovals.get(i);
|
||||
RecyclerView.ViewHolder item = mPendingRemovals.get(i);
|
||||
dispatchRemoveFinished(item);
|
||||
mPendingRemovals.remove(i);
|
||||
}
|
||||
count = mPendingAdditions.size();
|
||||
for (int i = count - 1; i >= 0; i--) {
|
||||
ViewHolder item = mPendingAdditions.get(i);
|
||||
RecyclerView.ViewHolder item = mPendingAdditions.get(i);
|
||||
item.itemView.setAlpha(1);
|
||||
dispatchAddFinished(item);
|
||||
mPendingAdditions.remove(i);
|
||||
|
@ -594,7 +602,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
|||
count = moves.size();
|
||||
for (int j = count - 1; j >= 0; j--) {
|
||||
MoveInfo moveInfo = moves.get(j);
|
||||
ViewHolder item = moveInfo.holder;
|
||||
RecyclerView.ViewHolder item = moveInfo.holder;
|
||||
View view = item.itemView;
|
||||
view.setTranslationY(0);
|
||||
view.setTranslationX(0);
|
||||
|
@ -607,10 +615,10 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
|||
}
|
||||
listCount = mAdditionsList.size();
|
||||
for (int i = listCount - 1; i >= 0; i--) {
|
||||
ArrayList<ViewHolder> additions = mAdditionsList.get(i);
|
||||
ArrayList<RecyclerView.ViewHolder> additions = mAdditionsList.get(i);
|
||||
count = additions.size();
|
||||
for (int j = count - 1; j >= 0; j--) {
|
||||
ViewHolder item = additions.get(j);
|
||||
RecyclerView.ViewHolder item = additions.get(j);
|
||||
View view = item.itemView;
|
||||
view.setAlpha(1);
|
||||
dispatchAddFinished(item);
|
||||
|
@ -640,7 +648,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
|
|||
dispatchAnimationsFinished();
|
||||
}
|
||||
|
||||
void cancelAll(List<ViewHolder> viewHolders) {
|
||||
void cancelAll(List<RecyclerView.ViewHolder> viewHolders) {
|
||||
for (int i = viewHolders.size() - 1; i >= 0; i--) {
|
||||
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>.
|
||||
* When this is the case:
|
||||
* <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.
|
||||
* </li>
|
||||
* <li>
|
||||
* If you are not overriding {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)},
|
||||
* then DefaultItemAnimator will call {@link #animateMove(ViewHolder, int, int, int, int)} and
|
||||
* If you are not overriding {@link #animateChange(RecyclerView.ViewHolder, RecyclerView.ViewHolder, int, int, int, int)},
|
||||
* then DefaultItemAnimator will call {@link #animateMove(RecyclerView.ViewHolder, int, int, int, int)} and
|
||||
* run a move animation instead.
|
||||
* </li>
|
||||
* </ul>
|
||||
*/
|
||||
@Override
|
||||
public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
|
||||
public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder,
|
||||
@NonNull List<Object> 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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -14,11 +14,12 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.telegram.messenger.support.util;
|
||||
package androidx.recyclerview.widget;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import org.telegram.messenger.support.widget.RecyclerView;
|
||||
import androidx.annotation.IntRange;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
@ -27,18 +28,23 @@ import java.util.Comparator;
|
|||
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.
|
||||
* <p>
|
||||
* It can be used to calculate updates for a RecyclerView Adapter. See
|
||||
* {@link android.support.v7.recyclerview.extensions.ListAdapter} and
|
||||
* {@link android.support.v7.recyclerview.extensions.AsyncListDiffer} which can compute diffs using
|
||||
* DiffUtil on a background thread.
|
||||
* It can be used to calculate updates for a RecyclerView Adapter. See {@link ListAdapter} and
|
||||
* {@link AsyncListDiffer} which can simplify the use of DiffUtil on a background thread.
|
||||
* <p>
|
||||
* 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
|
||||
* DiffUtil runs a second pass on the result to detect items that were moved.
|
||||
* <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
|
||||
* on a background thread, get the {@link DiffResult} then apply it on the RecyclerView on the main
|
||||
* thread.
|
||||
|
@ -66,7 +72,8 @@ import java.util.List;
|
|||
* <p>
|
||||
* 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 {
|
||||
|
||||
|
@ -93,7 +100,8 @@ public class DiffUtil {
|
|||
* @return A DiffResult that contains the information about the edit sequence to convert the
|
||||
* old list into the new list.
|
||||
*/
|
||||
public static DiffResult calculateDiff(Callback cb) {
|
||||
@NonNull
|
||||
public static DiffResult calculateDiff(@NonNull Callback cb) {
|
||||
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
|
||||
* 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 newSize = cb.getNewListSize();
|
||||
|
||||
|
@ -316,7 +325,7 @@ public class DiffUtil {
|
|||
* DiffUtil uses this method to check equality instead of {@link Object#equals(Object)}
|
||||
* so that you can change its behavior depending on your UI.
|
||||
* For example, if you are using 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.
|
||||
* <p>
|
||||
* This method is called only if {@link #areItemsTheSame(int, int)} returns
|
||||
|
@ -336,7 +345,7 @@ public class DiffUtil {
|
|||
* <p>
|
||||
* For example, if you are using DiffUtil with {@link RecyclerView}, you can return the
|
||||
* 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.
|
||||
* <p>
|
||||
* Default implementation returns {@code null}.
|
||||
|
@ -366,6 +375,10 @@ public class DiffUtil {
|
|||
* Called to check whether two objects represent the same item.
|
||||
* <p>
|
||||
* 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 newItem The item in the new list.
|
||||
|
@ -373,7 +386,7 @@ public class DiffUtil {
|
|||
*
|
||||
* @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.
|
||||
|
@ -384,11 +397,14 @@ public class DiffUtil {
|
|||
* change its behavior depending on your UI.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* This method is called only if {@link #areItemsTheSame(T, T)} returns {@code true} for
|
||||
* 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 newItem The item in the new list.
|
||||
|
@ -396,7 +412,7 @@ public class DiffUtil {
|
|||
*
|
||||
* @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
|
||||
|
@ -405,7 +421,7 @@ public class DiffUtil {
|
|||
* <p>
|
||||
* For example, if you are using DiffUtil with {@link RecyclerView}, you can return the
|
||||
* 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.
|
||||
* <p>
|
||||
* Default implementation returns {@code null}.
|
||||
|
@ -413,7 +429,8 @@ public class DiffUtil {
|
|||
* @see Callback#getChangePayload(int, int)
|
||||
*/
|
||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
public Object getChangePayload(T oldItem, T newItem) {
|
||||
@Nullable
|
||||
public Object getChangePayload(@NonNull T oldItem, @NonNull T newItem) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -482,6 +499,12 @@ public class DiffUtil {
|
|||
* {@link RecyclerView.Adapter} via {@link #dispatchUpdatesTo(RecyclerView.Adapter)}.
|
||||
*/
|
||||
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,
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* (before : left and above).
|
||||
|
@ -698,7 +769,7 @@ public class DiffUtil {
|
|||
/**
|
||||
* Dispatches the update events to the given adapter.
|
||||
* <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
|
||||
* method to dispatch all updates to the RecyclerView.
|
||||
* <pre>
|
||||
|
@ -714,11 +785,11 @@ public class DiffUtil {
|
|||
* before RecyclerView tries to read it.
|
||||
* <p>
|
||||
* 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
|
||||
* list is instantly moved to its final state while the adapter updates are dispatched later
|
||||
* on, one by one. If you have such an
|
||||
* {@link android.support.v7.widget.RecyclerView.AdapterDataObserver AdapterDataObserver},
|
||||
* {@link RecyclerView.AdapterDataObserver AdapterDataObserver},
|
||||
* you can use
|
||||
* {@link #dispatchUpdatesTo(ListUpdateCallback)} to handle each modification
|
||||
* manually.
|
||||
|
@ -727,7 +798,7 @@ public class DiffUtil {
|
|||
* displaying the new list.
|
||||
* @see AdapterListUpdateCallback
|
||||
*/
|
||||
public void dispatchUpdatesTo(final RecyclerView.Adapter adapter) {
|
||||
public void dispatchUpdatesTo(@NonNull final RecyclerView.Adapter adapter) {
|
||||
dispatchUpdatesTo(new AdapterListUpdateCallback(adapter));
|
||||
}
|
||||
|
||||
|
@ -740,7 +811,7 @@ public class DiffUtil {
|
|||
* @param updateCallback The callback to receive the update operations.
|
||||
* @see #dispatchUpdatesTo(RecyclerView.Adapter)
|
||||
*/
|
||||
public void dispatchUpdatesTo(ListUpdateCallback updateCallback) {
|
||||
public void dispatchUpdatesTo(@NonNull ListUpdateCallback updateCallback) {
|
||||
final BatchingListUpdateCallback batchingCallback;
|
||||
if (updateCallback instanceof BatchingListUpdateCallback) {
|
||||
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");
|
||||
* 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.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
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
|
||||
* between items of a {@link LinearLayoutManager}. It supports both {@link #HORIZONTAL} and
|
||||
|
@ -98,6 +100,14 @@ public class DividerItemDecoration extends RecyclerView.ItemDecoration {
|
|||
mDivider = drawable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link Drawable} for this divider.
|
||||
*/
|
||||
@Nullable
|
||||
public Drawable getDrawable() {
|
||||
return mDivider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
|
||||
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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.telegram.messenger.support.widget;
|
||||
package androidx.recyclerview.widget;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
|
@ -23,15 +23,14 @@ import android.animation.ValueAnimator.AnimatorUpdateListener;
|
|||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.Drawable;
|
||||
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 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.RetentionPolicy;
|
||||
|
||||
|
@ -39,7 +38,7 @@ import java.lang.annotation.RetentionPolicy;
|
|||
* Class responsible to animate and provide a fast scroller.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
class FastScroller extends ItemDecoration implements OnItemTouchListener {
|
||||
class FastScroller extends RecyclerView.ItemDecoration implements RecyclerView.OnItemTouchListener {
|
||||
@IntDef({STATE_HIDDEN, STATE_VISIBLE, STATE_DRAGGING})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
private @interface State { }
|
||||
|
@ -79,8 +78,10 @@ class FastScroller extends ItemDecoration implements OnItemTouchListener {
|
|||
private final int mMargin;
|
||||
|
||||
// Final values for the vertical scroll bar
|
||||
private final StateListDrawable mVerticalThumbDrawable;
|
||||
private final Drawable mVerticalTrackDrawable;
|
||||
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||
final StateListDrawable mVerticalThumbDrawable;
|
||||
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||
final Drawable mVerticalTrackDrawable;
|
||||
private final int mVerticalThumbWidth;
|
||||
private final int mVerticalTrackWidth;
|
||||
|
||||
|
@ -115,15 +116,18 @@ class FastScroller extends ItemDecoration implements OnItemTouchListener {
|
|||
|
||||
private final int[] mVerticalRange = new int[2];
|
||||
private final int[] mHorizontalRange = new int[2];
|
||||
private final ValueAnimator mShowHideAnimator = ValueAnimator.ofFloat(0, 1);
|
||||
@AnimationState private int mAnimationState = ANIMATION_STATE_OUT;
|
||||
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||
final ValueAnimator mShowHideAnimator = ValueAnimator.ofFloat(0, 1);
|
||||
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||
@AnimationState int mAnimationState = ANIMATION_STATE_OUT;
|
||||
private final Runnable mHideRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
hide(HIDE_DURATION_MS);
|
||||
}
|
||||
};
|
||||
private final OnScrollListener mOnScrollListener = new OnScrollListener() {
|
||||
private final RecyclerView.OnScrollListener
|
||||
mOnScrollListener = new RecyclerView.OnScrollListener() {
|
||||
@Override
|
||||
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
||||
updateScrollPosition(recyclerView.computeHorizontalScrollOffset(),
|
||||
|
@ -182,11 +186,12 @@ class FastScroller extends ItemDecoration implements OnItemTouchListener {
|
|||
cancelHide();
|
||||
}
|
||||
|
||||
private void requestRedraw() {
|
||||
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||
void requestRedraw() {
|
||||
mRecyclerView.invalidate();
|
||||
}
|
||||
|
||||
private void setState(@State int state) {
|
||||
void setState(@State int state) {
|
||||
if (state == STATE_DRAGGING && mState != STATE_DRAGGING) {
|
||||
mVerticalThumbDrawable.setState(PRESSED_STATE_SET);
|
||||
cancelHide();
|
||||
|
@ -219,11 +224,6 @@ class FastScroller extends ItemDecoration implements OnItemTouchListener {
|
|||
return mState == STATE_VISIBLE;
|
||||
}
|
||||
|
||||
@VisibleForTesting boolean isHidden() {
|
||||
return mState == STATE_HIDDEN;
|
||||
}
|
||||
|
||||
|
||||
public void show() {
|
||||
switch (mAnimationState) {
|
||||
case ANIMATION_STATE_FADING_OUT:
|
||||
|
@ -239,10 +239,6 @@ class FastScroller extends ItemDecoration implements OnItemTouchListener {
|
|||
}
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
hide(0);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void hide(int duration) {
|
||||
switch (mAnimationState) {
|
||||
|
@ -379,7 +375,8 @@ class FastScroller extends ItemDecoration implements OnItemTouchListener {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent ev) {
|
||||
public boolean onInterceptTouchEvent(@NonNull RecyclerView recyclerView,
|
||||
@NonNull MotionEvent ev) {
|
||||
final boolean handled;
|
||||
if (mState == STATE_VISIBLE) {
|
||||
boolean insideVerticalThumb = isPointInsideVerticalThumb(ev.getX(), ev.getY());
|
||||
|
@ -408,7 +405,7 @@ class FastScroller extends ItemDecoration implements OnItemTouchListener {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onTouchEvent(RecyclerView recyclerView, MotionEvent me) {
|
||||
public void onTouchEvent(@NonNull RecyclerView recyclerView, @NonNull MotionEvent me) {
|
||||
if (mState == STATE_HIDDEN) {
|
||||
return;
|
||||
}
|
||||
|
@ -551,6 +548,9 @@ class FastScroller extends ItemDecoration implements OnItemTouchListener {
|
|||
|
||||
private boolean mCanceled = false;
|
||||
|
||||
AnimatorListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
// 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 {
|
||||
AnimatorUpdater() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator valueAnimator) {
|