1
0
mirror of https://github.com/MGislv/NekoX.git synced 2024-06-28 09:34:05 +00:00

Update to 5.6.1

This commit is contained in:
DrKLO 2019-05-14 15:08:05 +03:00
parent e397bd9afd
commit 2cf2a45aca
3181 changed files with 41027 additions and 27918 deletions

View File

@ -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'
@ -89,11 +92,11 @@ android {
debugMultidex {
initWith debug
minifyEnabled false
multiDexEnabled true
dependencies{
multiDexEnabled true
dependencies {
implementation 'com.android.support:multidex:1.0.3'
}
manifestPlaceholders = [applicationClassName:"MultiDexApplicationLoader"]
manifestPlaceholders = [applicationClassName: "MultiDexApplicationLoader"]
}
HA {
@ -115,15 +118,13 @@ android {
}
}
defaultConfig.versionCode = 1517
sourceSets.debug {
manifest.srcFile 'config/debug/AndroidManifest.xml'
}
sourceSets.debugMultidex {
manifest.srcFile 'config/debug/AndroidManifest.xml'
java.srcDirs = ['src/multidex/java']
java.srcDirs = ['src/multidex/java']
}
sourceSets.HA {
@ -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,9 +277,9 @@ android {
defaultConfig {
minSdkVersion 16
targetSdkVersion 27
versionName "5.4.0"
versionName "5.6.1"
vectorDrawables.generatedDensities = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi']
vectorDrawables.generatedDensities = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi']
externalNativeBuild {
ndkBuild {
@ -275,7 +288,7 @@ android {
}
}
manifestPlaceholders = [applicationClassName:"ApplicationLoader"]
manifestPlaceholders = [applicationClassName: "ApplicationLoader"]
}
}

View File

@ -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"

View File

@ -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"

View File

@ -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>

View File

@ -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"

View File

@ -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"

View File

@ -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 \

View File

@ -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) {

View File

@ -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) {
@ -527,10 +444,6 @@ extern "C" int registerNativeTgNetFunctions(JavaVM *vm, JNIEnv *env) {
if (!registerNativeMethods(env, NativeByteBufferClassPathName, NativeByteBufferMethods, sizeof(NativeByteBufferMethods) / sizeof(NativeByteBufferMethods[0]))) {
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;
}

View File

@ -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;
}
}

View File

@ -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();

File diff suppressed because it is too large Load Diff

View File

@ -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);

View 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 */

View 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 */

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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&nbsp;&nbsp;</b>
** <td>&rarr;&nbsp;&nbsp;<td>True if the column is unchanged in an UPDATE
** against a virtual table.
** <tr><td><b>sqlite3_value_frombind&nbsp;&nbsp;</b>
** <td>&rarr;&nbsp;&nbsp;<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.

View File

@ -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;
}

View File

@ -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

View File

@ -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) {

View File

@ -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++;

View File

@ -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;

View File

@ -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,32 +97,28 @@ 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());
#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());
closeSocket(1, -1);
return;
}
struct in_addr **addr_list = (struct in_addr **) he->h_addr_list;
if (addr_list[0] != nullptr) {
socketAddress.sin_addr.s_addr = addr_list[0]->s_addr;
if (LOGS_ENABLED) DEBUG_D("connection(%p) resolved host %s address %x", this, proxyAddress->c_str(), addr_list[0]->s_addr);
ipv6 = false;
} else {
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) {
struct hostent *he;
if ((he = gethostbyname(proxyAddress->c_str())) == nullptr) {
if (LOGS_ENABLED) DEBUG_E("connection(%p) can't resolve host %s address", this, proxyAddress->c_str());
closeSocket(1, -1);
return;
}
struct in_addr **addr_list = (struct in_addr **) he->h_addr_list;
if (addr_list[0] != nullptr) {
socketAddress.sin_addr.s_addr = addr_list[0]->s_addr;
if (LOGS_ENABLED) DEBUG_D("connection(%p) resolved host %s address %x", this, proxyAddress->c_str(), addr_list[0]->s_addr);
ipv6 = false;
} else {
if (LOGS_ENABLED) DEBUG_E("connection(%p) can't resolve host %s address", this, proxyAddress->c_str());
closeSocket(1, -1);
return;
}
if (LOGS_ENABLED) DEBUG_E("connection(%p) can't resolve host %s address", this, proxyAddress->c_str());
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;
}
@ -447,4 +462,19 @@ void ConnectionSocket::setOverrideProxy(std::string address, uint16_t port, std:
overrideProxyUser = username;
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);
}
});
}

View File

@ -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;

View File

@ -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,35 +678,33 @@ 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()) {
bool found = false;
for (requestsIter iter2 = runningRequests.begin(); iter2 != runningRequests.end(); iter2++) {
Request *request = iter2->get();
if (connection->getConnectionToken() == request->connectionToken && request->requestToken == proxyCheckInfo->requestToken && (request->connectionType & 0x0000ffff) == ConnectionTypeProxy) {
request->completed = true;
runningRequests.erase(iter2);
proxyCheckInfo->onRequestTime(-1);
found = true;
break;
}
for (std::vector<std::unique_ptr<ProxyCheckInfo>>::iterator iter = proxyActiveChecks.begin(); iter != proxyActiveChecks.end(); iter++) {
ProxyCheckInfo *proxyCheckInfo = iter->get();
if (proxyCheckInfo->connectionNum == connection->getConnectionNum()) {
bool found = false;
for (requestsIter iter2 = runningRequests.begin(); iter2 != runningRequests.end(); iter2++) {
Request *request = iter2->get();
if (connection->getConnectionToken() == request->connectionToken && request->requestToken == proxyCheckInfo->requestToken && (request->connectionType & 0x0000ffff) == ConnectionTypeProxy) {
request->completed = true;
runningRequests.erase(iter2);
proxyCheckInfo->onRequestTime(-1);
found = true;
break;
}
if (found) {
proxyActiveChecks.erase(iter);
if (!proxyCheckQueue.empty()) {
proxyCheckInfo = proxyCheckQueue[0].release();
proxyCheckQueue.erase(proxyCheckQueue.begin());
checkProxyInternal(proxyCheckInfo);
}
}
break;
}
if (found) {
proxyActiveChecks.erase(iter);
if (!proxyCheckQueue.empty()) {
proxyCheckInfo = proxyCheckQueue[0].release();
proxyCheckQueue.erase(proxyCheckQueue.begin());
checkProxyInternal(proxyCheckInfo);
}
}
break;
}
});
}
}
}
@ -2365,7 +2372,7 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t
} else {
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();

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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

View File

@ -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.**

View File

@ -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>

View File

@ -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

View File

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 353 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 315 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 316 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

@ -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;

View File

@ -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.

View File

@ -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;
}
}

View File

@ -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&lt;List&lt;User>> usersByLastName();
* }
*
* class MyViewModel extends ViewModel {
* public final LiveData&lt;List&lt;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&lt;UserViewHolder> {
* private final AsyncListDiffer&lt;User> mDiffer = new AsyncListDiffer(this, DIFF_CALLBACK);
* {@literal @}Override
* public int getItemCount() {
* return mDiffer.getCurrentList().size();
* }
* public void submitList(List&lt;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&lt;User> DIFF_CALLBACK
* = new DiffUtil.ItemCallback&lt;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);
}
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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;

View File

@ -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) {

View File

@ -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) {

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