Update to 3.7.0

This commit is contained in:
DrKLO 2016-03-16 15:26:32 +03:00
parent 6154c891bd
commit a7513b3ba1
145 changed files with 12568 additions and 5776 deletions

View File

@ -1,6 +1,6 @@
## Telegram messenger for Android
[Telegram](http://telegram.org) is a messaging app with a focus on speed and security. Its superfast, simple and free.
[Telegram](https://telegram.org) is a messaging app with a focus on speed and security. Its superfast, simple and free.
This repo contains the official source code for [Telegram App for Android](https://play.google.com/store/apps/details?id=org.telegram.messenger).
##Creating your Telegram Application
@ -16,9 +16,9 @@ There are several things we require from **all developers** for the moment.
### API, Protocol documentation
Telegram API manuals: http://core.telegram.org/api
Telegram API manuals: https://core.telegram.org/api
MTproto protocol manuals: http://core.telegram.org/mtproto
MTproto protocol manuals: https://core.telegram.org/mtproto
### Usage

View File

@ -5,7 +5,7 @@ repositories {
}
dependencies {
compile 'com.android.support:support-v4:23.1.+'
compile 'com.android.support:support-v4:23.2.1'
compile "com.google.android.gms:play-services-gcm:8.4.0"
compile "com.google.android.gms:play-services-maps:8.4.0"
compile 'net.hockeyapp.android:HockeySDK:3.6.+'
@ -63,6 +63,8 @@ android {
}
}
defaultConfig.versionCode = 767
sourceSets.main {
jniLibs.srcDir 'libs'
jni.srcDirs = [] //disable automatic ndk-build call
@ -80,10 +82,38 @@ android {
manifest.srcFile 'config/foss/AndroidManifest.xml'
}
productFlavors {
x86 {
ndk {
abiFilter "x86"
}
versionCode = 2
}
arm {
ndk {
abiFilter "armeabi"
}
versionCode = 0
}
armv7 {
ndk {
abiFilter "armeabi-v7a"
}
versionCode = 1
}
fat {
versionCode = 3
}
}
applicationVariants.all { variant ->
def abiVersion = variant.productFlavors.get(0).versionCode
variant.mergedFlavor.versionCode = defaultConfig.versionCode * 10 + abiVersion;
}
defaultConfig {
minSdkVersion 9
targetSdkVersion 23
versionCode 755
versionName "3.6.1"
versionName "3.7.0"
}
}

View File

@ -235,7 +235,7 @@ include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE := tmessages.19
LOCAL_MODULE := tmessages.20
LOCAL_CFLAGS := -w -std=c11 -Os -DNULL=0 -DSOCKLEN_T=socklen_t -DLOCALE_NOT_USED -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64
LOCAL_CFLAGS += -Drestrict='' -D__EMX__ -DOPUS_BUILD -DFIXED_POINT -DUSE_ALLOCA -DHAVE_LRINT -DHAVE_LRINTF -fno-math-errno
LOCAL_CFLAGS += -DANDROID_NDK -DDISABLE_IMPORTGL -fno-strict-aliasing -fprefetch-loop-arrays -DAVOID_TABLES -DANDROID_TILE_BASED_DECODE -DANDROID_ARMV6_IDCT -ffast-math -D__STDC_CONSTANT_MACROS

View File

@ -1,4 +1,4 @@
APP_PLATFORM := android-9
APP_ABI := armeabi armeabi-v7a x86
NDK_TOOLCHAIN_VERSION := 4.8
NDK_TOOLCHAIN_VERSION := 4.9
APP_STL := gnustl_static

View File

@ -671,8 +671,7 @@ JNIEXPORT int Java_org_telegram_messenger_MediaController_isOpusFile(JNIEnv *env
return result;
}
static inline void set_bits(uint8_t *bytes, int32_t bitOffset, int32_t numBits, int32_t value) {
numBits = (unsigned int) (2 << (numBits - 1)) - 1;
static inline void set_bits(uint8_t *bytes, int32_t bitOffset, int32_t value) {
bytes += bitOffset / 8;
bitOffset %= 8;
*((int32_t *) bytes) |= (value << bitOffset);
@ -727,7 +726,7 @@ JNIEXPORT jbyteArray Java_org_telegram_messenger_MediaController_getWaveform2(JN
for (int i = 0; i < resultSamples; i++) {
int32_t value = min(31, abs((int32_t) samples[i]) * 31 / peak);
set_bits(bytes, i * 5, 5, value & 31);
set_bits(bytes, i * 5, value & 31);
}
(*env)->ReleaseByteArrayElements(env, result, bytes, JNI_COMMIT);
@ -805,7 +804,7 @@ JNIEXPORT jbyteArray Java_org_telegram_messenger_MediaController_getWaveform(JNI
for (int i = 0; i < resultSamples; i++) {
int32_t value = min(31, abs((int32_t) samples[i]) * 31 / peak);
set_bits(bytes, i * 5, 5, value & 31);
set_bits(bytes, i * 5, value & 31);
}
(*env)->ReleaseByteArrayElements(env, result, bytes, JNI_COMMIT);

View File

@ -5,6 +5,7 @@
#include <inttypes.h>
#include <stdlib.h>
#include <openssl/aes.h>
#include <unistd.h>
#include "utils.h"
#include "sqlite.h"
#include "image.h"

View File

@ -8,8 +8,8 @@ jint sqliteOnJNILoad(JavaVM *vm, void *reserved, JNIEnv *env) {
return JNI_VERSION_1_6;
}
int Java_org_telegram_SQLite_SQLitePreparedStatement_step(JNIEnv* env, jobject object, int statementHandle) {
sqlite3_stmt *handle = (sqlite3_stmt *)statementHandle;
int Java_org_telegram_SQLite_SQLitePreparedStatement_step(JNIEnv *env, jobject object, int statementHandle) {
sqlite3_stmt *handle = (sqlite3_stmt *) statementHandle;
int errcode = sqlite3_step(handle);
if (errcode == SQLITE_ROW) {
@ -23,7 +23,7 @@ int Java_org_telegram_SQLite_SQLitePreparedStatement_step(JNIEnv* env, jobject o
}
int Java_org_telegram_SQLite_SQLitePreparedStatement_prepare(JNIEnv *env, jobject object, int sqliteHandle, jstring sql) {
sqlite3* handle = (sqlite3 *)sqliteHandle;
sqlite3 *handle = (sqlite3 *) sqliteHandle;
char const *sqlStr = (*env)->GetStringUTFChars(env, sql, 0);
@ -41,11 +41,11 @@ int Java_org_telegram_SQLite_SQLitePreparedStatement_prepare(JNIEnv *env, jobjec
(*env)->ReleaseStringUTFChars(env, sql, sqlStr);
}
return (int)stmt_handle;
return (int) stmt_handle;
}
void Java_org_telegram_SQLite_SQLitePreparedStatement_reset(JNIEnv *env, jobject object, int statementHandle) {
sqlite3_stmt *handle = (sqlite3_stmt *)statementHandle;
sqlite3_stmt *handle = (sqlite3_stmt *) statementHandle;
int errcode = sqlite3_reset(handle);
if (SQLITE_OK != errcode) {
@ -54,16 +54,11 @@ void Java_org_telegram_SQLite_SQLitePreparedStatement_reset(JNIEnv *env, jobject
}
void Java_org_telegram_SQLite_SQLitePreparedStatement_finalize(JNIEnv *env, jobject object, int statementHandle) {
sqlite3_stmt *handle = (sqlite3_stmt *)statementHandle;
int errcode = sqlite3_finalize (handle);
if (SQLITE_OK != errcode) {
throw_sqlite3_exception(env, sqlite3_db_handle(handle), errcode);
}
sqlite3_finalize((sqlite3_stmt *) statementHandle);
}
void Java_org_telegram_SQLite_SQLitePreparedStatement_bindByteBuffer(JNIEnv *env, jobject object, int statementHandle, int index, jobject value, int length) {
sqlite3_stmt *handle = (sqlite3_stmt *)statementHandle;
sqlite3_stmt *handle = (sqlite3_stmt *) statementHandle;
jbyte *buf = (*env)->GetDirectBufferAddress(env, value);
int errcode = sqlite3_bind_blob(handle, index, buf, length, SQLITE_STATIC);
@ -73,7 +68,7 @@ void Java_org_telegram_SQLite_SQLitePreparedStatement_bindByteBuffer(JNIEnv *env
}
void Java_org_telegram_SQLite_SQLitePreparedStatement_bindString(JNIEnv *env, jobject object, int statementHandle, int index, jstring value) {
sqlite3_stmt *handle = (sqlite3_stmt*)statementHandle;
sqlite3_stmt *handle = (sqlite3_stmt *) statementHandle;
char const *valueStr = (*env)->GetStringUTFChars(env, value, 0);
@ -88,7 +83,7 @@ void Java_org_telegram_SQLite_SQLitePreparedStatement_bindString(JNIEnv *env, jo
}
void Java_org_telegram_SQLite_SQLitePreparedStatement_bindInt(JNIEnv *env, jobject object, int statementHandle, int index, int value) {
sqlite3_stmt *handle = (sqlite3_stmt*)statementHandle;
sqlite3_stmt *handle = (sqlite3_stmt *) statementHandle;
int errcode = sqlite3_bind_int(handle, index, value);
if (SQLITE_OK != errcode) {
@ -97,7 +92,7 @@ void Java_org_telegram_SQLite_SQLitePreparedStatement_bindInt(JNIEnv *env, jobje
}
void Java_org_telegram_SQLite_SQLitePreparedStatement_bindLong(JNIEnv *env, jobject object, int statementHandle, int index, long long value) {
sqlite3_stmt *handle = (sqlite3_stmt*)statementHandle;
sqlite3_stmt *handle = (sqlite3_stmt *) statementHandle;
int errcode = sqlite3_bind_int64(handle, index, value);
if (SQLITE_OK != errcode) {
@ -105,8 +100,8 @@ void Java_org_telegram_SQLite_SQLitePreparedStatement_bindLong(JNIEnv *env, jobj
}
}
void Java_org_telegram_SQLite_SQLitePreparedStatement_bindDouble(JNIEnv* env, jobject object, int statementHandle, int index, double value) {
sqlite3_stmt *handle = (sqlite3_stmt*)statementHandle;
void Java_org_telegram_SQLite_SQLitePreparedStatement_bindDouble(JNIEnv *env, jobject object, int statementHandle, int index, double value) {
sqlite3_stmt *handle = (sqlite3_stmt *) statementHandle;
int errcode = sqlite3_bind_double(handle, index, value);
if (SQLITE_OK != errcode) {
@ -114,8 +109,8 @@ void Java_org_telegram_SQLite_SQLitePreparedStatement_bindDouble(JNIEnv* env, jo
}
}
void Java_org_telegram_SQLite_SQLitePreparedStatement_bindNull(JNIEnv* env, jobject object, int statementHandle, int index) {
sqlite3_stmt *handle = (sqlite3_stmt*)statementHandle;
void Java_org_telegram_SQLite_SQLitePreparedStatement_bindNull(JNIEnv *env, jobject object, int statementHandle, int index) {
sqlite3_stmt *handle = (sqlite3_stmt *) statementHandle;
int errcode = sqlite3_bind_null(handle, index);
if (SQLITE_OK != errcode) {

View File

@ -57,7 +57,7 @@ void Connection::suspendConnection() {
}
void Connection::onReceivedData(NativeByteBuffer *buffer) {
//AES_ctr128_encrypt(buffer->bytes(), buffer->bytes(), buffer->limit(), &decryptKey, decryptIv, decryptCount, &decryptNum);
AES_ctr128_encrypt(buffer->bytes(), buffer->bytes(), buffer->limit(), &decryptKey, decryptIv, decryptCount, &decryptNum);
failedConnectionCount = 0;
@ -323,11 +323,11 @@ void Connection::sendData(NativeByteBuffer *buff, bool reportAck) {
uint32_t val = (bytes[3] << 24) | (bytes[2] << 16) | (bytes[1] << 8) | (bytes[0]);
uint32_t val2 = (bytes[7] << 24) | (bytes[6] << 16) | (bytes[5] << 8) | (bytes[4]);
if (bytes[0] != 0xef && val != 0x44414548 && val != 0x54534f50 && val != 0x20544547 && val != 0x4954504f && val != 0xeeeeeeee && val2 != 0x00000000) {
//bytes[56] = bytes[57] = bytes[58] = bytes[59] = 0xef;
bytes[56] = bytes[57] = bytes[58] = bytes[59] = 0xef;
break;
}
}
/*for (int a = 0; a < 48; a++) {
for (int a = 0; a < 48; a++) {
temp[a] = bytes[55 - a];
}
@ -348,7 +348,7 @@ void Connection::sendData(NativeByteBuffer *buff, bool reportAck) {
memcpy(decryptIv, temp + 32, 16);
AES_ctr128_encrypt(bytes, temp, 64, &encryptKey, encryptIv, encryptCount, &encryptNum);
memcpy(bytes + 56, temp + 56, 8);*/
memcpy(bytes + 56, temp + 56, 8);
firstPacketSent = true;
}
@ -358,7 +358,7 @@ void Connection::sendData(NativeByteBuffer *buff, bool reportAck) {
}
buffer->writeByte((uint8_t) packetLength);
bytes += (buffer->limit() - 1);
//AES_ctr128_encrypt(bytes, bytes, 1, &encryptKey, encryptIv, encryptCount, &encryptNum);
AES_ctr128_encrypt(bytes, bytes, 1, &encryptKey, encryptIv, encryptCount, &encryptNum);
} else {
packetLength = (packetLength << 8) + 0x7f;
if (reportAck) {
@ -366,13 +366,13 @@ void Connection::sendData(NativeByteBuffer *buff, bool reportAck) {
}
buffer->writeInt32(packetLength);
bytes += (buffer->limit() - 4);
//AES_ctr128_encrypt(bytes, bytes, 4, &encryptKey, encryptIv, encryptCount, &encryptNum);
AES_ctr128_encrypt(bytes, bytes, 4, &encryptKey, encryptIv, encryptCount, &encryptNum);
}
buffer->rewind();
writeBuffer(buffer);
buff->rewind();
//AES_ctr128_encrypt(buff->bytes(), buff->bytes(), buff->limit(), &encryptKey, encryptIv, encryptCount, &encryptNum);
AES_ctr128_encrypt(buff->bytes(), buff->bytes(), buff->limit(), &encryptKey, encryptIv, encryptCount, &encryptNum);
writeBuffer(buff);
}

View File

@ -17,7 +17,7 @@
#define USE_DEBUG_SESSION false
#define READ_BUFFER_SIZE 1024 * 128
//#define DEBUG_VERSION
#define DEBUG_VERSION
#define DEFAULT_DATACENTER_ID INT_MAX
#define DC_UPDATE_TIME 60 * 60
#define DOWNLOAD_CONNECTIONS_COUNT 2

View File

@ -99,6 +99,8 @@
<category android:name="android.intent.category.BROWSABLE" />
<data android:host="telegram.me" android:scheme="http" />
<data android:host="telegram.me" android:scheme="https" />
<data android:host="telegram.dog" android:scheme="http" />
<data android:host="telegram.dog" android:scheme="https" />
</intent-filter>
<intent-filter android:icon="@drawable/ic_launcher" android:priority="1">
<action android:name="android.intent.action.VIEW" />

View File

@ -11,6 +11,7 @@ package org.telegram.messenger;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.ContentUris;
import android.content.Context;
import android.content.DialogInterface;
@ -19,6 +20,7 @@ import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
@ -26,6 +28,7 @@ import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Parcelable;
import android.provider.Browser;
@ -84,7 +87,9 @@ public class AndroidUtilities {
private static final Hashtable<String, Typeface> typefaceCache = new Hashtable<>();
private static int prevOrientation = -10;
private static boolean waitingForSms = false;
private static boolean waitingForCall = false;
private static final Object smsLock = new Object();
private static final Object callLock = new Object();
public static int statusBarHeight = 0;
public static float density = 1;
@ -240,6 +245,20 @@ public class AndroidUtilities {
}
}
public static boolean isWaitingForCall() {
boolean value;
synchronized (callLock) {
value = waitingForCall;
}
return value;
}
public static void setWaitingForCall(boolean value) {
synchronized (callLock) {
waitingForCall = value;
}
}
public static void showKeyboard(View view) {
if (view == null) {
return;
@ -422,11 +441,28 @@ public class AndroidUtilities {
if (context == null || uri == null) {
return;
}
try {
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.putExtra("android.support.customtabs.extra.SESSION", (Parcelable) null);
intent.putExtra("android.support.customtabs.extra.TOOLBAR_COLOR", 0xff54759e);
intent.putExtra("android.support.customtabs.extra.TITLE_VISIBILITY", 1);
if (MediaController.getInstance().canCustomTabs()) {
intent.putExtra("android.support.customtabs.extra.SESSION", (Parcelable) null);
intent.putExtra("android.support.customtabs.extra.TOOLBAR_COLOR", 0xff54759e);
intent.putExtra("android.support.customtabs.extra.TITLE_VISIBILITY", 1);
Intent actionIntent = new Intent(Intent.ACTION_SEND);
actionIntent.setType("text/plain");
actionIntent.putExtra(Intent.EXTRA_TEXT, uri.toString());
actionIntent.putExtra(Intent.EXTRA_SUBJECT, "");
PendingIntent pendingIntent = PendingIntent.getActivity(ApplicationLoader.applicationContext, 0, actionIntent, PendingIntent.FLAG_ONE_SHOT);
Bundle bundle = new Bundle();
bundle.putInt("android.support.customtabs.customaction.ID", 0);
bundle.putParcelable("android.support.customtabs.customaction.ICON", BitmapFactory.decodeResource(context.getResources(), R.drawable.abc_ic_menu_share_mtrl_alpha));
bundle.putString("android.support.customtabs.customaction.DESCRIPTION", LocaleController.getString("ShareFile", R.string.ShareFile));
bundle.putParcelable("android.support.customtabs.customaction.PENDING_INTENT", pendingIntent);
intent.putExtra("android.support.customtabs.extra.ACTION_BUTTON_BUNDLE", bundle);
intent.putExtra("android.support.customtabs.extra.TINT_ACTION_BUTTON", false);
}
intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
context.startActivity(intent);
} catch (Exception e) {
@ -917,7 +953,11 @@ public class AndroidUtilities {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
String value = cursor.getString(column_index);
if (value.startsWith("content://") || !value.startsWith("/") && !value.startsWith("file://")) {
return null;
}
return value;
}
} catch (Exception e) {
FileLog.e("tmessages", e);

View File

@ -37,6 +37,8 @@ import org.telegram.ui.Components.ForegroundDetector;
import java.io.File;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Locale;
public class ApplicationLoader extends Application {

View File

@ -10,8 +10,8 @@ package org.telegram.messenger;
public class BuildVars {
public static boolean DEBUG_VERSION = false;
public static int BUILD_VERSION = 753;
public static String BUILD_VERSION_STRING = "3.6";
public static int BUILD_VERSION = 767;
public static String BUILD_VERSION_STRING = "3.7";
public static int APP_ID = 0; //obtain your own APP_ID at https://core.telegram.org/api/obtaining_api_id
public static String APP_HASH = ""; //obtain your own APP_HASH at https://core.telegram.org/api/obtaining_api_id
public static String HOCKEY_APP_HASH = "your-hockeyapp-api-key-here";

View File

@ -11,20 +11,24 @@ package org.telegram.messenger;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import org.telegram.PhoneFormat.PhoneFormat;
public class CallReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, Intent intent) {
/*TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
telephony.listen(new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
if (state == 1 && incomingNumber != null && incomingNumber.length() > 0) {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.didReceiveCall, incomingNumber);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.didReceiveCall, PhoneFormat.stripExceptNumbers(incomingNumber));
}
}
}, PhoneStateListener.LISTEN_CALL_STATE);*/
}, PhoneStateListener.LISTEN_CALL_STATE);
}
}

View File

@ -265,9 +265,9 @@ public class Emoji {
b = getBounds();
}
if (!canvas.quickReject(b.left, b.top, b.right, b.bottom, Canvas.EdgeType.AA)) {
//if (!canvas.quickReject(b.left, b.top, b.right, b.bottom, Canvas.EdgeType.AA)) {
canvas.drawBitmap(emojiBmp[info.page][info.page2], info.rect, b, paint);
}
//}
}
@Override

View File

@ -550,13 +550,17 @@ public class FileLoader {
return getAttachFileName(sizeFull);
}
}
} else if (message.media instanceof TLRPC.TL_messageMediaWebPage && message.media.webpage.photo != null) {
ArrayList<TLRPC.PhotoSize> sizes = message.media.webpage.photo.sizes;
if (sizes.size() > 0) {
TLRPC.PhotoSize sizeFull = getClosestPhotoSizeWithSize(sizes, AndroidUtilities.getPhotoSize());
if (sizeFull != null) {
return getAttachFileName(sizeFull);
} else if (message.media instanceof TLRPC.TL_messageMediaWebPage) {
if (message.media.webpage.photo != null) {
ArrayList<TLRPC.PhotoSize> sizes = message.media.webpage.photo.sizes;
if (sizes.size() > 0) {
TLRPC.PhotoSize sizeFull = getClosestPhotoSizeWithSize(sizes, AndroidUtilities.getPhotoSize());
if (sizeFull != null) {
return getAttachFileName(sizeFull);
}
}
} else if (message.media.webpage.document != null) {
return getAttachFileName(message.media.webpage.document);
}
}
}
@ -588,13 +592,17 @@ public class FileLoader {
return getPathToAttach(sizeFull);
}
}
} else if (message.media instanceof TLRPC.TL_messageMediaWebPage && message.media.webpage.photo != null) {
ArrayList<TLRPC.PhotoSize> sizes = message.media.webpage.photo.sizes;
if (sizes.size() > 0) {
TLRPC.PhotoSize sizeFull = getClosestPhotoSizeWithSize(sizes, AndroidUtilities.getPhotoSize());
if (sizeFull != null) {
return getPathToAttach(sizeFull);
} else if (message.media instanceof TLRPC.TL_messageMediaWebPage) {
if (message.media.webpage.photo != null) {
ArrayList<TLRPC.PhotoSize> sizes = message.media.webpage.photo.sizes;
if (sizes.size() > 0) {
TLRPC.PhotoSize sizeFull = getClosestPhotoSizeWithSize(sizes, AndroidUtilities.getPhotoSize());
if (sizeFull != null) {
return getPathToAttach(sizeFull);
}
}
} else if (message.media.webpage.document != null) {
return getPathToAttach(message.media.webpage.document);
}
}
}
@ -704,6 +712,23 @@ public class FileLoader {
return "";
}
public static String getDocumentExtension(TLRPC.Document document) {
String fileName = getDocumentFileName(document);
int idx = fileName.lastIndexOf(".");
String ext = null;
if (idx != -1) {
ext = fileName.substring(idx + 1);
}
if (ext == null || ext.length() == 0) {
ext = document.mime_type;
}
if (ext == null) {
ext = "";
}
ext = ext.toUpperCase();
return ext;
}
public static String getAttachFileName(TLObject attach) {
return getAttachFileName(attach, null);
}

View File

@ -196,14 +196,17 @@ public class FileLog {
File sdCard = ApplicationLoader.applicationContext.getExternalFilesDir(null);
File dir = new File (sdCard.getAbsolutePath() + "/logs");
File[] files = dir.listFiles();
for (File file : files) {
if (getInstance().currentFile != null && file.getAbsolutePath().equals(getInstance().currentFile.getAbsolutePath())) {
continue;
if (files != null) {
for (int a = 0; a < files.length; a++) {
File file = files[a];
if (getInstance().currentFile != null && file.getAbsolutePath().equals(getInstance().currentFile.getAbsolutePath())) {
continue;
}
if (getInstance().networkFile != null && file.getAbsolutePath().equals(getInstance().networkFile.getAbsolutePath())) {
continue;
}
file.delete();
}
if (getInstance().networkFile != null && file.getAbsolutePath().equals(getInstance().networkFile.getAbsolutePath())) {
continue;
}
file.delete();
}
}
}

View File

@ -23,7 +23,6 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;
import org.telegram.tgnet.ConnectionsManager;
@ -33,7 +32,6 @@ import org.telegram.ui.Components.AnimatedFileDrawable;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
@ -2031,8 +2029,7 @@ public class ImageLoader {
public static Bitmap loadBitmap(String path, Uri uri, float maxWidth, float maxHeight, boolean useMaxScale) {
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
FileDescriptor fileDescriptor = null;
ParcelFileDescriptor parcelFD = null;
InputStream inputStream = null;
if (path == null && uri != null && uri.getScheme() != null) {
String imageFilePath = null;
@ -2052,9 +2049,10 @@ public class ImageLoader {
} else if (uri != null) {
boolean error = false;
try {
parcelFD = ApplicationLoader.applicationContext.getContentResolver().openFileDescriptor(uri, "r");
fileDescriptor = parcelFD.getFileDescriptor();
BitmapFactory.decodeFileDescriptor(fileDescriptor, null, bmOptions);
inputStream = ApplicationLoader.applicationContext.getContentResolver().openInputStream(uri);
BitmapFactory.decodeStream(inputStream, null, bmOptions);
inputStream.close();
inputStream = ApplicationLoader.applicationContext.getContentResolver().openInputStream(uri);
} catch (Throwable e) {
FileLog.e("tmessages", e);
return null;
@ -2138,7 +2136,7 @@ public class ImageLoader {
}
} else if (uri != null) {
try {
b = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, bmOptions);
b = BitmapFactory.decodeStream(inputStream, null, bmOptions);
if (b != null) {
if (bmOptions.inPurgeable) {
Utilities.pinBitmap(b);
@ -2153,7 +2151,7 @@ public class ImageLoader {
FileLog.e("tmessages", e);
} finally {
try {
parcelFD.close();
inputStream.close();
} catch (Throwable e) {
FileLog.e("tmessages", e);
}

View File

@ -322,7 +322,7 @@ public class LocaleController {
StringBuilder result = new StringBuilder(11);
result.append(languageCode);
if (countryCode.length() > 0 || variantCode.length() > 0) {
result.append('_');
result.append('-');
}
result.append(countryCode);
if (variantCode.length() > 0) {
@ -664,16 +664,21 @@ public class LocaleController {
}
public static String formatDateChat(long date) {
Calendar rightNow = Calendar.getInstance();
int year = rightNow.get(Calendar.YEAR);
try {
Calendar rightNow = Calendar.getInstance();
int year = rightNow.get(Calendar.YEAR);
rightNow.setTimeInMillis(date * 1000);
int dateYear = rightNow.get(Calendar.YEAR);
rightNow.setTimeInMillis(date * 1000);
int dateYear = rightNow.get(Calendar.YEAR);
if (year == dateYear) {
return getInstance().chatDate.format(date * 1000);
if (year == dateYear) {
return getInstance().chatDate.format(date * 1000);
}
return getInstance().chatFullDate.format(date * 1000);
} catch (Exception e) {
FileLog.e("tmessages", e);
}
return getInstance().chatFullDate.format(date * 1000);
return "LOC_ERR: formatDateChat";
}
public static String formatDate(long date) {
@ -697,7 +702,7 @@ public class LocaleController {
} catch (Exception e) {
FileLog.e("tmessages", e);
}
return "LOC_ERR";
return "LOC_ERR: formatDate";
}
public static String formatDateAudio(long date) {

View File

@ -42,10 +42,10 @@ import android.net.ConnectivityManager;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.Vibrator;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
@ -63,6 +63,7 @@ import org.telegram.ui.PhotoViewer;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@ -235,7 +236,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
private float[] gravityFast = new float[3];
private float[] linearAcceleration = new float[3];
private boolean hasAudioFoces;
private boolean hasAudioFocus;
private boolean callInProgress;
private ArrayList<MessageObject> videoConvertQueue = new ArrayList<>();
@ -269,6 +270,8 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
private boolean saveToGallery = true;
private boolean autoplayGifs = true;
private boolean raiseToSpeak = true;
private boolean customTabs = true;
private boolean directShare = true;
private boolean shuffleMusic;
private int repeatMode;
@ -595,6 +598,8 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
saveToGallery = preferences.getBoolean("save_gallery", false);
autoplayGifs = preferences.getBoolean("autoplay_gif", true) && Build.VERSION.SDK_INT >= 11;
raiseToSpeak = preferences.getBoolean("raise_to_speak", true) && Build.VERSION.SDK_INT >= 11;
customTabs = preferences.getBoolean("custom_tabs", true);
directShare = preferences.getBoolean("direct_share", true);
shuffleMusic = preferences.getBoolean("shuffleMusic", false);
repeatMode = preferences.getInt("repeatMode", 0);
@ -689,7 +694,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
if (MediaController.getInstance().isPlayingAudio(MediaController.getInstance().getPlayingMessageObject()) && !MediaController.getInstance().isAudioPaused()) {
MediaController.getInstance().pauseAudio(MediaController.getInstance().getPlayingMessageObject());
}
hasAudioFoces = false;
hasAudioFocus = false;
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
//MediaController.getInstance().playAudio(MediaController.getInstance().getPlayingMessageObject());
}
@ -1653,14 +1658,24 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
if (playingMessageObject == null) {
return;
}
boolean post = audioPlayer != null;
NotificationCenter.getInstance().postNotificationName(NotificationCenter.audioRouteChanged, useFrontSpeaker);
MessageObject currentMessageObject = playingMessageObject;
final MessageObject currentMessageObject = playingMessageObject;
float progress = playingMessageObject.audioProgress;
cleanupPlayer(false, true);
currentMessageObject.audioProgress = progress;
playAudio(currentMessageObject);
if (paused) {
pauseAudio(currentMessageObject);
if (post) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
pauseAudio(currentMessageObject);
}
}, 100);
} else {
pauseAudio(currentMessageObject);
}
}
}
@ -1959,6 +1974,11 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
if (byStop && repeatMode == 0) {
if (audioPlayer != null || audioTrackPlayer != null) {
if (audioPlayer != null) {
try {
audioPlayer.reset();
} catch (Exception e) {
FileLog.e("tmessages", e);
}
try {
audioPlayer.stop();
} catch (Exception e) {
@ -2058,7 +2078,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
}
public boolean playAudio(MessageObject messageObject) {
public boolean playAudio(final MessageObject messageObject) {
if (messageObject == null) {
return false;
}
@ -2171,7 +2191,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
if (!playlist.isEmpty() && playlist.size() > 1) {
playNextMessage(true);
} else {
cleanupPlayer(true, true);
cleanupPlayer(true, true, messageObject != null && messageObject.isVoice());
}
}
});
@ -2201,8 +2221,8 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
return false;
}
}
if (!hasAudioFoces) {
hasAudioFoces = true;
if (!hasAudioFocus) {
hasAudioFocus = true;
NotificationsController.getInstance().audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
}
@ -2270,6 +2290,11 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
try {
if (audioPlayer != null) {
try {
audioPlayer.reset();
} catch (Exception e) {
FileLog.e("tmessages", e);
}
audioPlayer.stop();
} else if (audioTrackPlayer != null) {
audioTrackPlayer.pause();
@ -2378,8 +2403,8 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
audioTrackPlayer.play();
checkPlayerQueue();
}
if (!hasAudioFoces) {
hasAudioFoces = true;
if (!hasAudioFocus) {
hasAudioFocus = true;
NotificationsController.getInstance().audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
}
isPaused = false;
@ -2507,19 +2532,6 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
public void generateWaveform(MessageObject messageObject) {
final String id = messageObject.getId() + "_" + messageObject.getDialogId();
final String path = FileLoader.getPathToMessage(messageObject.messageOwner).getAbsolutePath();
/*for (int a = 0; a < currentMessageObject.messageOwner.media.document.attributes.size(); a++) { TODO if old attribute
TLRPC.DocumentAttribute attribute = currentMessageObject.messageOwner.media.document.attributes.get(a);
if (attribute instanceof TLRPC.TL_documentAttributeAudio) {
if (attribute.waveform == null || attribute.waveform.length == 0) {
attribute.waveform = MediaController.getInstance().getWaveform(path.getAbsolutePath());
}
if (attribute.waveform != null) {
hasWaveform = true;
}
seekBarWaveform.setWaveform(attribute.waveform);
break;
}
}*/
if (generatingWaveform.containsKey(id)) {
return;
}
@ -2764,14 +2776,11 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
public static boolean isWebp(Uri uri) {
ParcelFileDescriptor parcelFD = null;
FileInputStream input = null;
InputStream inputStream = null;
try {
parcelFD = ApplicationLoader.applicationContext.getContentResolver().openFileDescriptor(uri, "r");
input = new FileInputStream(parcelFD.getFileDescriptor());
if (input.getChannel().size() > 12) {
byte[] header = new byte[12];
input.read(header, 0, 12);
inputStream = ApplicationLoader.applicationContext.getContentResolver().openInputStream(uri);
byte[] header = new byte[12];
if (inputStream.read(header, 0, 12) == 12) {
String str = new String(header);
if (str != null) {
str = str.toLowerCase();
@ -2784,15 +2793,8 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
FileLog.e("tmessages", e);
} finally {
try {
if (parcelFD != null) {
parcelFD.close();
}
} catch (Exception e2) {
FileLog.e("tmessages", e2);
}
try {
if (input != null) {
input.close();
if (inputStream != null) {
inputStream.close();
}
} catch (Exception e2) {
FileLog.e("tmessages", e2);
@ -2802,14 +2804,11 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
public static boolean isGif(Uri uri) {
ParcelFileDescriptor parcelFD = null;
FileInputStream input = null;
InputStream inputStream = null;
try {
parcelFD = ApplicationLoader.applicationContext.getContentResolver().openFileDescriptor(uri, "r");
input = new FileInputStream(parcelFD.getFileDescriptor());
if (input.getChannel().size() > 3) {
byte[] header = new byte[3];
input.read(header, 0, 3);
inputStream = ApplicationLoader.applicationContext.getContentResolver().openInputStream(uri);
byte[] header = new byte[3];
if (inputStream.read(header, 0, 3) == 3) {
String str = new String(header);
if (str != null && str.equalsIgnoreCase("gif")) {
return true;
@ -2819,15 +2818,8 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
FileLog.e("tmessages", e);
} finally {
try {
if (parcelFD != null) {
parcelFD.close();
}
} catch (Exception e2) {
FileLog.e("tmessages", e2);
}
try {
if (input != null) {
input.close();
if (inputStream != null) {
inputStream.close();
}
} catch (Exception e2) {
FileLog.e("tmessages", e2);
@ -2836,33 +2828,58 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
return false;
}
public static String copyDocumentToCache(Uri uri, String ext) {
ParcelFileDescriptor parcelFD = null;
FileInputStream input = null;
public static String getFileName(Uri uri) {
String result = null;
if (uri.getScheme().equals("content")) {
Cursor cursor = ApplicationLoader.applicationContext.getContentResolver().query(uri, null, null, null, null);
try {
if (cursor.moveToFirst()) {
result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
}
} catch (Exception e) {
FileLog.e("tmessages", e);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
if (result == null) {
result = uri.getPath();
int cut = result.lastIndexOf('/');
if (cut != -1) {
result = result.substring(cut + 1);
}
}
return result;
}
public static String copyFileToCache(Uri uri, String ext) {
InputStream inputStream = null;
FileOutputStream output = null;
try {
int id = UserConfig.lastLocalId;
UserConfig.lastLocalId--;
parcelFD = ApplicationLoader.applicationContext.getContentResolver().openFileDescriptor(uri, "r");
input = new FileInputStream(parcelFD.getFileDescriptor());
File f = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), String.format(Locale.US, "%d.%s", id, ext));
String name = getFileName(uri);
if (name == null) {
int id = UserConfig.lastLocalId;
UserConfig.lastLocalId--;
UserConfig.saveConfig(false);
name = String.format(Locale.US, "%d.%s", id, ext);
}
inputStream = ApplicationLoader.applicationContext.getContentResolver().openInputStream(uri);
File f = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), name);
output = new FileOutputStream(f);
input.getChannel().transferTo(0, input.getChannel().size(), output.getChannel());
UserConfig.saveConfig(false);
byte[] buffer = new byte[1024 * 20];
int len;
while ((len = inputStream.read(buffer)) != -1) {
output.write(buffer, 0, len);
}
return f.getAbsolutePath();
} catch (Exception e) {
FileLog.e("tmessages", e);
} finally {
try {
if (parcelFD != null) {
parcelFD.close();
}
} catch (Exception e2) {
FileLog.e("tmessages", e2);
}
try {
if (input != null) {
input.close();
if (inputStream != null) {
inputStream.close();
}
} catch (Exception e2) {
FileLog.e("tmessages", e2);
@ -2903,6 +2920,22 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
editor.commit();
}
public void toggleCustomTabs() {
customTabs = !customTabs;
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean("custom_tabs", customTabs);
editor.commit();
}
public void toggleDirectShare() {
directShare = !directShare;
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean("direct_share", directShare);
editor.commit();
}
public void checkSaveToGalleryFiles() {
try {
File telegramPath = new File(Environment.getExternalStorageDirectory(), "Telegram");
@ -2943,6 +2976,14 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
return raiseToSpeak;
}
public boolean canCustomTabs() {
return customTabs;
}
public boolean canDirectShare() {
return directShare;
}
public static void loadGalleryPhotosAlbums(final int guid) {
new Thread(new Runnable() {
@Override
@ -3246,6 +3287,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
long startTime = -1;
checkConversionCanceled();
long lastTimestamp = -100;
while (!inputDone) {
checkConversionCanceled();
@ -3254,28 +3296,37 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
int index = extractor.getSampleTrackIndex();
if (index == trackIndex) {
info.size = extractor.readSampleData(buffer, 0);
if (info.size < 0) {
if (info.size >= 0) {
info.presentationTimeUs = extractor.getSampleTime();
} else {
info.size = 0;
eof = true;
} else {
info.presentationTimeUs = extractor.getSampleTime();
}
if (info.size > 0 && !eof) {
if (start > 0 && startTime == -1) {
startTime = info.presentationTimeUs;
}
if (end < 0 || info.presentationTimeUs < end) {
info.offset = 0;
info.flags = extractor.getSampleFlags();
if (mediaMuxer.writeSampleData(muxerTrackIndex, buffer, info, isAudio)) {
didWriteData(messageObject, file, false, false);
if (info.presentationTimeUs > lastTimestamp) {
info.offset = 0;
info.flags = extractor.getSampleFlags();
if (mediaMuxer.writeSampleData(muxerTrackIndex, buffer, info, isAudio)) {
didWriteData(messageObject, file, false, false);
}
}
extractor.advance();
lastTimestamp = info.presentationTimeUs;
} else {
eof = true;
}
}
if (!eof) {
extractor.advance();
}
} else if (index == -1) {
eof = true;
} else {
extractor.advance();
}
if (eof) {
inputDone = true;

View File

@ -58,11 +58,10 @@ public class MessageObject {
public VideoEditedInfo videoEditedInfo;
public boolean viewsReloaded;
public static TextPaint textPaint;
private static TextPaint textPaint;
public int lastLineWidth;
public int textWidth;
public int textHeight;
public int blockHeight = Integer.MAX_VALUE;
private boolean layoutCreated;
@ -70,9 +69,10 @@ public class MessageObject {
public static class TextLayoutBlock {
public StaticLayout textLayout;
public float textXOffset = 0;
public float textYOffset = 0;
public int charactersOffset = 0;
public float textXOffset;
public float textYOffset;
public int charactersOffset;
public int height;
}
private static final int LINES_PER_BLOCK = 10;
@ -99,7 +99,7 @@ public class MessageObject {
}
TLRPC.User fromUser = null;
if (isFromUser()) {
if (message.from_id > 0) {
if (users != null) {
fromUser = users.get(message.from_id);
}
@ -114,22 +114,14 @@ public class MessageObject {
if (isOut()) {
messageText = LocaleController.getString("ActionYouCreateGroup", R.string.ActionYouCreateGroup);
} else {
if (fromUser != null) {
messageText = replaceWithLink(LocaleController.getString("ActionCreateGroup", R.string.ActionCreateGroup), "un1", fromUser);
} else {
messageText = LocaleController.getString("ActionCreateGroup", R.string.ActionCreateGroup).replace("un1", "");
}
messageText = replaceWithLink(LocaleController.getString("ActionCreateGroup", R.string.ActionCreateGroup), "un1", fromUser);
}
} else if (message.action instanceof TLRPC.TL_messageActionChatDeleteUser) {
if (message.action.user_id == message.from_id) {
if (isOut()) {
messageText = LocaleController.getString("ActionYouLeftUser", R.string.ActionYouLeftUser);
} else {
if (fromUser != null) {
messageText = replaceWithLink(LocaleController.getString("ActionLeftUser", R.string.ActionLeftUser), "un1", fromUser);
} else {
messageText = LocaleController.getString("ActionLeftUser", R.string.ActionLeftUser).replace("un1", "");
}
messageText = replaceWithLink(LocaleController.getString("ActionLeftUser", R.string.ActionLeftUser), "un1", fromUser);
}
} else {
TLRPC.User whoUser = null;
@ -139,17 +131,13 @@ public class MessageObject {
if (whoUser == null) {
whoUser = MessagesController.getInstance().getUser(message.action.user_id);
}
if (whoUser != null && fromUser != null) {
if (isOut()) {
messageText = replaceWithLink(LocaleController.getString("ActionYouKickUser", R.string.ActionYouKickUser), "un2", whoUser);
} else if (message.action.user_id == UserConfig.getClientUserId()) {
messageText = replaceWithLink(LocaleController.getString("ActionKickUserYou", R.string.ActionKickUserYou), "un1", fromUser);
} else {
messageText = replaceWithLink(LocaleController.getString("ActionKickUser", R.string.ActionKickUser), "un2", whoUser);
messageText = replaceWithLink(messageText, "un1", fromUser);
}
if (isOut()) {
messageText = replaceWithLink(LocaleController.getString("ActionYouKickUser", R.string.ActionYouKickUser), "un2", whoUser);
} else if (message.action.user_id == UserConfig.getClientUserId()) {
messageText = replaceWithLink(LocaleController.getString("ActionKickUserYou", R.string.ActionKickUserYou), "un1", fromUser);
} else {
messageText = LocaleController.getString("ActionKickUser", R.string.ActionKickUser).replace("un2", "").replace("un1", "");
messageText = replaceWithLink(LocaleController.getString("ActionKickUser", R.string.ActionKickUser), "un2", whoUser);
messageText = replaceWithLink(messageText, "un1", fromUser);
}
}
} else if (message.action instanceof TLRPC.TL_messageActionChatAddUser) {
@ -165,36 +153,38 @@ public class MessageObject {
if (whoUser == null) {
whoUser = MessagesController.getInstance().getUser(singleUserId);
}
if (message.to_id.channel_id != 0 && !isMegagroup()) {
if (whoUser != null && whoUser.id != UserConfig.getClientUserId()) {
if (isMegagroup()) {
messageText = replaceWithLink(LocaleController.getString("MegaAddedBy", R.string.MegaAddedBy), "un1", whoUser);
} else {
messageText = replaceWithLink(LocaleController.getString("ChannelAddedBy", R.string.ChannelAddedBy), "un1", whoUser);
}
} else {
if (singleUserId == message.from_id) {
if (message.to_id.channel_id != 0 && !isMegagroup()) {
messageText = LocaleController.getString("ChannelJoined", R.string.ChannelJoined);
} else {
if (message.to_id.channel_id != 0 && isMegagroup()) {
if (singleUserId == UserConfig.getClientUserId()) {
messageText = LocaleController.getString("ChannelMegaJoined", R.string.ChannelMegaJoined);
} else {
messageText = replaceWithLink(LocaleController.getString("ActionAddUserSelfMega", R.string.ActionAddUserSelfMega), "un1", fromUser);
}
} else if (isOut()) {
messageText = LocaleController.getString("ActionAddUserSelfYou", R.string.ActionAddUserSelfYou);
} else {
messageText = replaceWithLink(LocaleController.getString("ActionAddUserSelf", R.string.ActionAddUserSelf), "un1", fromUser);
}
}
} else {
if (whoUser != null && fromUser != null) {
if (whoUser.id == fromUser.id) {
if (isOut()) {
messageText = LocaleController.getString("ActionAddUserSelfYou", R.string.ActionAddUserSelfYou);
if (isOut()) {
messageText = replaceWithLink(LocaleController.getString("ActionYouAddUser", R.string.ActionYouAddUser), "un2", whoUser);
} else if (singleUserId == UserConfig.getClientUserId()) {
if (message.to_id.channel_id != 0) {
if (isMegagroup()) {
messageText = replaceWithLink(LocaleController.getString("MegaAddedBy", R.string.MegaAddedBy), "un1", fromUser);
} else {
messageText = replaceWithLink(LocaleController.getString("ActionAddUserSelf", R.string.ActionAddUserSelf), "un1", fromUser);
messageText = replaceWithLink(LocaleController.getString("ChannelAddedBy", R.string.ChannelAddedBy), "un1", fromUser);
}
} else {
if (isOut()) {
messageText = replaceWithLink(LocaleController.getString("ActionYouAddUser", R.string.ActionYouAddUser), "un2", whoUser);
} else if (singleUserId == UserConfig.getClientUserId()) {
messageText = replaceWithLink(LocaleController.getString("ActionAddUserYou", R.string.ActionAddUserYou), "un1", fromUser);
} else {
messageText = replaceWithLink(LocaleController.getString("ActionAddUser", R.string.ActionAddUser), "un2", whoUser);
messageText = replaceWithLink(messageText, "un1", fromUser);
}
messageText = replaceWithLink(LocaleController.getString("ActionAddUserYou", R.string.ActionAddUserYou), "un1", fromUser);
}
} else {
messageText = LocaleController.getString("ActionAddUser", R.string.ActionAddUser).replace("un2", "").replace("un1", "");
messageText = replaceWithLink(LocaleController.getString("ActionAddUser", R.string.ActionAddUser), "un2", whoUser);
messageText = replaceWithLink(messageText, "un1", fromUser);
}
}
} else {
@ -206,14 +196,10 @@ public class MessageObject {
}
}
} else if (message.action instanceof TLRPC.TL_messageActionChatJoinedByLink) {
if (fromUser != null) {
if (isOut()) {
messageText = LocaleController.getString("ActionInviteYou", R.string.ActionInviteYou);
} else {
messageText = replaceWithLink(LocaleController.getString("ActionInviteUser", R.string.ActionInviteUser), "un1", fromUser);
}
if (isOut()) {
messageText = LocaleController.getString("ActionInviteYou", R.string.ActionInviteYou);
} else {
messageText = LocaleController.getString("ActionInviteUser", R.string.ActionInviteUser).replace("un1", "");
messageText = replaceWithLink(LocaleController.getString("ActionInviteUser", R.string.ActionInviteUser), "un1", fromUser);
}
} else if (message.action instanceof TLRPC.TL_messageActionChatEditPhoto) {
if (message.to_id.channel_id != 0 && !isMegagroup()) {
@ -222,11 +208,7 @@ public class MessageObject {
if (isOut()) {
messageText = LocaleController.getString("ActionYouChangedPhoto", R.string.ActionYouChangedPhoto);
} else {
if (fromUser != null) {
messageText = replaceWithLink(LocaleController.getString("ActionChangedPhoto", R.string.ActionChangedPhoto), "un1", fromUser);
} else {
messageText = LocaleController.getString("ActionChangedPhoto", R.string.ActionChangedPhoto).replace("un1", "");
}
messageText = replaceWithLink(LocaleController.getString("ActionChangedPhoto", R.string.ActionChangedPhoto), "un1", fromUser);
}
}
} else if (message.action instanceof TLRPC.TL_messageActionChatEditTitle) {
@ -236,11 +218,7 @@ public class MessageObject {
if (isOut()) {
messageText = LocaleController.getString("ActionYouChangedTitle", R.string.ActionYouChangedTitle).replace("un2", message.action.title);
} else {
if (fromUser != null) {
messageText = replaceWithLink(LocaleController.getString("ActionChangedTitle", R.string.ActionChangedTitle).replace("un2", message.action.title), "un1", fromUser);
} else {
messageText = LocaleController.getString("ActionChangedTitle", R.string.ActionChangedTitle).replace("un1", "").replace("un2", message.action.title);
}
messageText = replaceWithLink(LocaleController.getString("ActionChangedTitle", R.string.ActionChangedTitle).replace("un2", message.action.title), "un1", fromUser);
}
}
} else if (message.action instanceof TLRPC.TL_messageActionChatDeletePhoto) {
@ -250,11 +228,7 @@ public class MessageObject {
if (isOut()) {
messageText = LocaleController.getString("ActionYouRemovedPhoto", R.string.ActionYouRemovedPhoto);
} else {
if (fromUser != null) {
messageText = replaceWithLink(LocaleController.getString("ActionRemovedPhoto", R.string.ActionRemovedPhoto), "un1", fromUser);
} else {
messageText = LocaleController.getString("ActionRemovedPhoto", R.string.ActionRemovedPhoto).replace("un1", "");
}
messageText = replaceWithLink(LocaleController.getString("ActionRemovedPhoto", R.string.ActionRemovedPhoto), "un1", fromUser);
}
}
} else if (message.action instanceof TLRPC.TL_messageActionTTLChange) {
@ -262,21 +236,13 @@ public class MessageObject {
if (isOut()) {
messageText = LocaleController.formatString("MessageLifetimeChangedOutgoing", R.string.MessageLifetimeChangedOutgoing, AndroidUtilities.formatTTLString(message.action.ttl));
} else {
if (fromUser != null) {
messageText = LocaleController.formatString("MessageLifetimeChanged", R.string.MessageLifetimeChanged, UserObject.getFirstName(fromUser), AndroidUtilities.formatTTLString(message.action.ttl));
} else {
messageText = LocaleController.formatString("MessageLifetimeChanged", R.string.MessageLifetimeChanged, "", AndroidUtilities.formatTTLString(message.action.ttl));
}
messageText = LocaleController.formatString("MessageLifetimeChanged", R.string.MessageLifetimeChanged, UserObject.getFirstName(fromUser), AndroidUtilities.formatTTLString(message.action.ttl));
}
} else {
if (isOut()) {
messageText = LocaleController.getString("MessageLifetimeYouRemoved", R.string.MessageLifetimeYouRemoved);
} else {
if (fromUser != null) {
messageText = LocaleController.formatString("MessageLifetimeRemoved", R.string.MessageLifetimeRemoved, UserObject.getFirstName(fromUser));
} else {
messageText = LocaleController.formatString("MessageLifetimeRemoved", R.string.MessageLifetimeRemoved, "");
}
messageText = LocaleController.formatString("MessageLifetimeRemoved", R.string.MessageLifetimeRemoved, UserObject.getFirstName(fromUser));
}
}
} else if (message.action instanceof TLRPC.TL_messageActionLoginUnknownLocation) {
@ -299,27 +265,15 @@ public class MessageObject {
String name = to_user != null ? UserObject.getFirstName(to_user) : "";
messageText = LocaleController.formatString("NotificationUnrecognizedDevice", R.string.NotificationUnrecognizedDevice, name, date, message.action.title, message.action.address);
} else if (message.action instanceof TLRPC.TL_messageActionUserJoined) {
if (fromUser != null) {
messageText = LocaleController.formatString("NotificationContactJoined", R.string.NotificationContactJoined, UserObject.getUserName(fromUser));
} else {
messageText = LocaleController.formatString("NotificationContactJoined", R.string.NotificationContactJoined, "");
}
messageText = LocaleController.formatString("NotificationContactJoined", R.string.NotificationContactJoined, UserObject.getUserName(fromUser));
} else if (message.action instanceof TLRPC.TL_messageActionUserUpdatedPhoto) {
if (fromUser != null) {
messageText = LocaleController.formatString("NotificationContactNewPhoto", R.string.NotificationContactNewPhoto, UserObject.getUserName(fromUser));
} else {
messageText = LocaleController.formatString("NotificationContactNewPhoto", R.string.NotificationContactNewPhoto, "");
}
messageText = LocaleController.formatString("NotificationContactNewPhoto", R.string.NotificationContactNewPhoto, UserObject.getUserName(fromUser));
} else if (message.action instanceof TLRPC.TL_messageEncryptedAction) {
if (message.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionScreenshotMessages) {
if (isOut()) {
messageText = LocaleController.formatString("ActionTakeScreenshootYou", R.string.ActionTakeScreenshootYou);
} else {
if (fromUser != null) {
messageText = replaceWithLink(LocaleController.getString("ActionTakeScreenshoot", R.string.ActionTakeScreenshoot), "un1", fromUser);
} else {
messageText = LocaleController.formatString("ActionTakeScreenshoot", R.string.ActionTakeScreenshoot).replace("un1", "");
}
messageText = replaceWithLink(LocaleController.getString("ActionTakeScreenshoot", R.string.ActionTakeScreenshoot), "un1", fromUser);
}
} else if (message.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionSetMessageTTL) {
TLRPC.TL_decryptedMessageActionSetMessageTTL action = (TLRPC.TL_decryptedMessageActionSetMessageTTL) message.action.encryptedAction;
@ -327,21 +281,13 @@ public class MessageObject {
if (isOut()) {
messageText = LocaleController.formatString("MessageLifetimeChangedOutgoing", R.string.MessageLifetimeChangedOutgoing, AndroidUtilities.formatTTLString(action.ttl_seconds));
} else {
if (fromUser != null) {
messageText = LocaleController.formatString("MessageLifetimeChanged", R.string.MessageLifetimeChanged, UserObject.getFirstName(fromUser), AndroidUtilities.formatTTLString(action.ttl_seconds));
} else {
messageText = LocaleController.formatString("MessageLifetimeChanged", R.string.MessageLifetimeChanged, "", AndroidUtilities.formatTTLString(action.ttl_seconds));
}
messageText = LocaleController.formatString("MessageLifetimeChanged", R.string.MessageLifetimeChanged, UserObject.getFirstName(fromUser), AndroidUtilities.formatTTLString(action.ttl_seconds));
}
} else {
if (isOut()) {
messageText = LocaleController.getString("MessageLifetimeYouRemoved", R.string.MessageLifetimeYouRemoved);
} else {
if (fromUser != null) {
messageText = LocaleController.formatString("MessageLifetimeRemoved", R.string.MessageLifetimeRemoved, UserObject.getFirstName(fromUser));
} else {
messageText = LocaleController.formatString("MessageLifetimeRemoved", R.string.MessageLifetimeRemoved, "");
}
messageText = LocaleController.formatString("MessageLifetimeRemoved", R.string.MessageLifetimeRemoved, UserObject.getFirstName(fromUser));
}
}
}
@ -357,6 +303,8 @@ public class MessageObject {
messageText = LocaleController.getString("ActionMigrateFromGroup", R.string.ActionMigrateFromGroup);
} else if (message.action instanceof TLRPC.TL_messageActionChannelMigrateFrom) {
messageText = LocaleController.getString("ActionMigrateFromGroup", R.string.ActionMigrateFromGroup);
} else if (message.action instanceof TLRPC.TL_messageActionPinMessage) {
generatePinMessageText(fromUser, fromUser == null ? chats.get(message.to_id.channel_id) : null);
}
}
} else if (!isMediaEmpty()) {
@ -402,27 +350,31 @@ public class MessageObject {
if (message instanceof TLRPC.TL_message || message instanceof TLRPC.TL_messageForwarded_old2) {
if (isMediaEmpty()) {
contentType = type = 0;
contentType = 0;
type = 0;
if (messageText == null || messageText.length() == 0) {
messageText = "Empty message";
}
} else if (message.media instanceof TLRPC.TL_messageMediaPhoto) {
contentType = type = 1;
contentType = 0;
type = 1;
} else if (message.media instanceof TLRPC.TL_messageMediaGeo || message.media instanceof TLRPC.TL_messageMediaVenue) {
contentType = 1;
contentType = 0;
type = 4;
} else if (isVideo()) {
contentType = 1;
contentType = 0;
type = 3;
} else if (isVoice()) {
contentType = type = 2;
contentType = 2;
type = 2;
} else if (message.media instanceof TLRPC.TL_messageMediaContact) {
contentType = 3;
type = 12;
} else if (message.media instanceof TLRPC.TL_messageMediaUnsupported) {
contentType = type = 0;
contentType = 0;
type = 0;
} else if (message.media instanceof TLRPC.TL_messageMediaDocument) {
contentType = 1;
contentType = 0;
if (message.media.document.mime_type != null) {
if (isGifDocument(message.media.document)) {
type = 8;
@ -440,7 +392,8 @@ public class MessageObject {
}
} else if (message instanceof TLRPC.TL_messageService) {
if (message.action instanceof TLRPC.TL_messageActionLoginUnknownLocation) {
contentType = type = 0;
contentType = 0;
type = 0;
} else if (message.action instanceof TLRPC.TL_messageActionChatEditPhoto || message.action instanceof TLRPC.TL_messageActionUserUpdatedPhoto) {
contentType = 4;
type = 11;
@ -464,7 +417,7 @@ public class MessageObject {
int dateYear = rightNow.get(Calendar.YEAR);
int dateMonth = rightNow.get(Calendar.MONTH);
dateKey = String.format("%d_%02d_%02d", dateYear, dateMonth, dateDay);
if (contentType == 1 || contentType == 2 || contentType == 0 || contentType == 8) {
if (contentType == 2 || contentType == 0 || contentType == 8) {
monthKey = String.format("%d_%02d", dateYear, dateMonth);
} else if (contentType == 9) {
//dateKey = "0_0_0";
@ -484,6 +437,58 @@ public class MessageObject {
generateThumbs(false);
}
public static TextPaint getTextPaint() {
if (textPaint == null) {
textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
textPaint.setColor(0xff000000);
textPaint.linkColor = 0xff316f9f;
textPaint.setTextSize(AndroidUtilities.dp(MessagesController.getInstance().fontSize));
}
return textPaint;
}
public void generatePinMessageText(TLRPC.User fromUser, TLRPC.Chat chat) {
if (fromUser == null && chat == null) {
if (messageOwner.from_id > 0) {
fromUser = MessagesController.getInstance().getUser(messageOwner.from_id);
}
if (fromUser == null) {
chat = MessagesController.getInstance().getChat(messageOwner.to_id.channel_id);
}
}
if (replyMessageObject == null) {
messageText = replaceWithLink(LocaleController.getString("ActionPinnedNoText", R.string.ActionPinnedNoText), "un1", fromUser != null ? fromUser : chat);
} else {
if (replyMessageObject.isMusic()) {
messageText = replaceWithLink(LocaleController.getString("ActionPinnedMusic", R.string.ActionPinnedMusic), "un1", fromUser != null ? fromUser : chat);
} else if (replyMessageObject.isVideo()) {
messageText = replaceWithLink(LocaleController.getString("ActionPinnedVideo", R.string.ActionPinnedVideo), "un1", fromUser != null ? fromUser : chat);
} else if (replyMessageObject.isGif()) {
messageText = replaceWithLink(LocaleController.getString("ActionPinnedGif", R.string.ActionPinnedGif), "un1", fromUser != null ? fromUser : chat);
} else if (replyMessageObject.isVoice()) {
messageText = replaceWithLink(LocaleController.getString("ActionPinnedVoice", R.string.ActionPinnedVoice), "un1", fromUser != null ? fromUser : chat);
} else if (replyMessageObject.isSticker()) {
messageText = replaceWithLink(LocaleController.getString("ActionPinnedSticker", R.string.ActionPinnedSticker), "un1", fromUser != null ? fromUser : chat);
} else if (replyMessageObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument) {
messageText = replaceWithLink(LocaleController.getString("ActionPinnedFile", R.string.ActionPinnedFile), "un1", fromUser != null ? fromUser : chat);
} else if (replyMessageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeo) {
messageText = replaceWithLink(LocaleController.getString("ActionPinnedGeo", R.string.ActionPinnedGeo), "un1", fromUser != null ? fromUser : chat);
} else if (replyMessageObject.messageOwner.media instanceof TLRPC.TL_messageMediaContact) {
messageText = replaceWithLink(LocaleController.getString("ActionPinnedContact", R.string.ActionPinnedContact), "un1", fromUser != null ? fromUser : chat);
} else if (replyMessageObject.messageOwner.media instanceof TLRPC.TL_messageMediaPhoto) {
messageText = replaceWithLink(LocaleController.getString("ActionPinnedPhoto", R.string.ActionPinnedPhoto), "un1", fromUser != null ? fromUser : chat);
} else if (replyMessageObject.messageText != null && replyMessageObject.messageText.length() > 0) {
CharSequence mess = replyMessageObject.messageText;
if (mess.length() > 20) {
mess = mess.subSequence(0, 20) + "...";
}
messageText = replaceWithLink(LocaleController.formatString("ActionPinnedText", R.string.ActionPinnedText, mess), "un1", fromUser != null ? fromUser : chat);
} else {
messageText = replaceWithLink(LocaleController.getString("ActionPinnedNoText", R.string.ActionPinnedNoText), "un1", fromUser != null ? fromUser : chat);
}
}
}
public void checkLayout() {
if (!layoutCreated) {
layoutCreated = true;
@ -662,6 +667,8 @@ public class MessageObject {
return FileLoader.getAttachFileName(sizeFull);
}
}
} else if (messageOwner.media instanceof TLRPC.TL_messageMediaWebPage) {
return FileLoader.getAttachFileName(messageOwner.media.webpage.document);
}
return "";
}
@ -950,7 +957,7 @@ public class MessageObject {
block.textLayout = textLayout;
block.textYOffset = 0;
block.charactersOffset = 0;
blockHeight = textHeight;
block.height = textHeight;
} else {
int startCharacter = textLayout.getLineStart(linesOffset);
int endCharacter = textLayout.getLineEnd(linesOffset + currentBlockLinesCount - 1);
@ -963,16 +970,10 @@ public class MessageObject {
block.textLayout = new StaticLayout(str, textPaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
block.textYOffset = textLayout.getLineTop(linesOffset);
if (a != 0) {
blockHeight = Math.min(blockHeight, (int) (block.textYOffset - prevOffset));
block.height = (int) (block.textYOffset - prevOffset);
}
block.height = Math.max(block.height, block.textLayout.getLineBottom(block.textLayout.getLineCount() - 1));
prevOffset = block.textYOffset;
/*if (a != blocksCount - 1) {
int height = block.textLayout.getHeight();
blockHeight = Math.min(blockHeight, block.textLayout.getHeight());
prevOffset = block.textYOffset;
} else {
blockHeight = Math.min(blockHeight, (int)(block.textYOffset - prevOffset));
}*/
} catch (Exception e) {
FileLog.e("tmessages", e);
continue;
@ -1067,9 +1068,6 @@ public class MessageObject {
linesOffset += currentBlockLinesCount;
}
if (blockHeight == 0) {
blockHeight = 1;
}
}
public boolean isOut() {
@ -1421,7 +1419,11 @@ public class MessageObject {
}
public boolean isGif() {
return isGifDocument(messageOwner.media.document);
return messageOwner.media instanceof TLRPC.TL_messageMediaDocument && isGifDocument(messageOwner.media.document);
}
public boolean isWebpageDocument() {
return messageOwner.media instanceof TLRPC.TL_messageMediaWebPage && messageOwner.media.webpage.document != null && !isGifDocument(messageOwner.media.webpage.document);
}
public boolean isNewGif() {
@ -1512,16 +1514,16 @@ public class MessageObject {
}
public static boolean canEditMessage(TLRPC.Message message, TLRPC.Chat chat) {
if (message.action != null && !(message.action instanceof TLRPC.TL_messageActionEmpty) || isForwardedMessage(message) || message.via_bot_id != 0 || message.id < 0 || Math.abs(message.date - ConnectionsManager.getInstance().getCurrentTime()) > MessagesController.getInstance().maxEditTime) {
if (message == null || message.to_id == null || message.to_id.channel_id == 0 || message.action != null && !(message.action instanceof TLRPC.TL_messageActionEmpty) || isForwardedMessage(message) || message.via_bot_id != 0 || message.id < 0 || Math.abs(message.date - ConnectionsManager.getInstance().getCurrentTime()) > MessagesController.getInstance().maxEditTime) {
return false;
}
if (chat == null && message.to_id.channel_id != 0) {
chat = MessagesController.getInstance().getChat(message.to_id.channel_id);
}
if (ChatObject.isChannel(chat) && chat.megagroup) {
return message.out;
if (chat == null) {
return false;
}
if (ChatObject.isChannel(chat) && !chat.megagroup && (chat.creator || chat.editor && isOut(message)) && isImportant(message)) {
if (chat.megagroup && message.out || !chat.megagroup && (chat.creator || chat.editor && isOut(message)) && isImportant(message)) {
if (message.media instanceof TLRPC.TL_messageMediaPhoto ||
message.media instanceof TLRPC.TL_messageMediaDocument && (isVideoMessage(message) || isGifDocument(message.media.document)) ||
message.media instanceof TLRPC.TL_messageMediaEmpty ||

View File

@ -19,6 +19,7 @@ import org.telegram.SQLite.SQLiteCursor;
import org.telegram.SQLite.SQLiteDatabase;
import org.telegram.SQLite.SQLitePreparedStatement;
import org.telegram.messenger.query.BotQuery;
import org.telegram.messenger.query.MessagesQuery;
import org.telegram.messenger.query.SharedMediaQuery;
import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.NativeByteBuffer;
@ -141,10 +142,16 @@ public class MessagesStorage {
database.executeFast("CREATE TABLE bot_keyboard(uid INTEGER PRIMARY KEY, mid INTEGER, info BLOB)").stepThis().dispose();
database.executeFast("CREATE INDEX IF NOT EXISTS bot_keyboard_idx_mid ON bot_keyboard(mid);").stepThis().dispose();
database.executeFast("CREATE TABLE chat_settings_v2(uid INTEGER PRIMARY KEY, info BLOB, pinned INTEGER)").stepThis().dispose();
database.executeFast("CREATE INDEX IF NOT EXISTS chat_settings_pinned_idx ON chat_settings_v2(uid, pinned) WHERE pinned != 0;").stepThis().dispose();
database.executeFast("CREATE TABLE chat_pinned(uid INTEGER PRIMARY KEY, pinned INTEGER, data BLOB)").stepThis().dispose();
database.executeFast("CREATE INDEX IF NOT EXISTS chat_pinned_mid_idx ON chat_pinned(uid, pinned) WHERE pinned != 0;").stepThis().dispose();
database.executeFast("CREATE TABLE users_data(uid INTEGER PRIMARY KEY, about TEXT)").stepThis().dispose();
database.executeFast("CREATE TABLE users(uid INTEGER PRIMARY KEY, name TEXT, status INTEGER, data BLOB)").stepThis().dispose();
database.executeFast("CREATE TABLE chats(uid INTEGER PRIMARY KEY, name TEXT, data BLOB)").stepThis().dispose();
database.executeFast("CREATE TABLE enc_chats(uid INTEGER PRIMARY KEY, user INTEGER, name TEXT, data BLOB, g BLOB, authkey BLOB, ttl INTEGER, layer INTEGER, seq_in INTEGER, seq_out INTEGER, use_count INTEGER, exchange_id INTEGER, key_date INTEGER, fprint INTEGER, fauthkey BLOB, khash BLOB)").stepThis().dispose();
database.executeFast("CREATE TABLE chat_settings_v2(uid INTEGER PRIMARY KEY, info BLOB)").stepThis().dispose();
database.executeFast("CREATE TABLE channel_users_v2(did INTEGER, uid INTEGER, date INTEGER, data BLOB, PRIMARY KEY(did, uid))").stepThis().dispose();
database.executeFast("CREATE TABLE contacts(uid INTEGER PRIMARY KEY, mutual INTEGER)").stepThis().dispose();
database.executeFast("CREATE TABLE pending_read(uid INTEGER PRIMARY KEY, max_id INTEGER)").stepThis().dispose();
@ -165,7 +172,7 @@ public class MessagesStorage {
database.executeFast("CREATE TABLE bot_info(uid INTEGER PRIMARY KEY, info BLOB)").stepThis().dispose();
//version
database.executeFast("PRAGMA user_version = 30").stepThis().dispose();
database.executeFast("PRAGMA user_version = 31").stepThis().dispose();
//database.executeFast("CREATE TABLE secret_holes(uid INTEGER, seq_in INTEGER, seq_out INTEGER, data BLOB, PRIMARY KEY (uid, seq_in, seq_out));").stepThis().dispose();
//database.executeFast("CREATE TABLE attach_data(uid INTEGER, id INTEGER, data BLOB, PRIMARY KEY (uid, id))").stepThis().dispose();
@ -199,7 +206,7 @@ public class MessagesStorage {
}
}
int version = database.executeInt("PRAGMA user_version");
if (version < 30) {
if (version < 31) {
updateDbToLastVersion(version);
}
}
@ -487,7 +494,16 @@ public class MessagesStorage {
database.executeFast("DELETE FROM sent_files_v2 WHERE 1").stepThis().dispose();
database.executeFast("DELETE FROM download_queue WHERE 1").stepThis().dispose();
database.executeFast("PRAGMA user_version = 30").stepThis().dispose();
//version = 30;
version = 30;
}
if (version == 30) {
database.executeFast("ALTER TABLE chat_settings_v2 ADD COLUMN pinned INTEGER default 0").stepThis().dispose();
database.executeFast("CREATE INDEX IF NOT EXISTS chat_settings_pinned_idx ON chat_settings_v2(uid, pinned) WHERE pinned != 0;").stepThis().dispose();
database.executeFast("CREATE TABLE IF NOT EXISTS chat_pinned(uid INTEGER PRIMARY KEY, pinned INTEGER, data BLOB)").stepThis().dispose();
database.executeFast("CREATE INDEX IF NOT EXISTS chat_pinned_mid_idx ON chat_pinned(uid, pinned) WHERE pinned != 0;").stepThis().dispose();
database.executeFast("CREATE TABLE IF NOT EXISTS users_data(uid INTEGER PRIMARY KEY, about TEXT)").stepThis().dispose();
database.executeFast("PRAGMA user_version = 31").stepThis().dispose();
//version = 31;
}
} catch (Exception e) {
FileLog.e("tmessages", e);
@ -1012,22 +1028,22 @@ public class MessagesStorage {
});
}
public void deleteDialog(final long did, final int messagesOnly) {
public void deleteUserChannelHistory(final int channelId, final int uid) {
storageQueue.postRunnable(new Runnable() {
@Override
public void run() {
try {
if ((int) did == 0 || messagesOnly == 2) {
SQLiteCursor cursor = database.queryFinalized("SELECT data FROM messages WHERE uid = " + did);
ArrayList<File> filesToDelete = new ArrayList<>();
try {
while (cursor.next()) {
NativeByteBuffer data = new NativeByteBuffer(cursor.byteArrayLength(0));
if (data != null && cursor.byteBufferValue(0, data) != 0) {
TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false);
if (message == null || message.media == null) {
continue;
}
long did = -channelId;
final ArrayList<Integer> mids = new ArrayList<>();
SQLiteCursor cursor = database.queryFinalized("SELECT data FROM messages WHERE uid = " + did);
ArrayList<File> filesToDelete = new ArrayList<>();
try {
while (cursor.next()) {
NativeByteBuffer data = new NativeByteBuffer(cursor.byteArrayLength(0));
if (data != null && cursor.byteBufferValue(0, data) != 0) {
TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false);
if (message != null && message.from_id == uid && message.id != 1) {
mids.add(message.id);
if (message.media instanceof TLRPC.TL_messageMediaPhoto) {
for (TLRPC.PhotoSize photoSize : message.media.photo.sizes) {
File file = FileLoader.getPathToAttach(photoSize);
@ -1046,6 +1062,70 @@ public class MessagesStorage {
}
}
}
}
data.reuse();
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
cursor.dispose();
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
MessagesController.getInstance().markChannelDialogMessageAsDeleted(mids, channelId);
}
});
markMessagesAsDeletedInternal(mids, channelId);
updateDialogsWithDeletedMessagesInternal(mids, channelId);
FileLoader.getInstance().deleteFiles(filesToDelete, 0);
if (!mids.isEmpty()) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.messagesDeleted, mids, channelId);
}
});
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
});
}
public void deleteDialog(final long did, final int messagesOnly) {
storageQueue.postRunnable(new Runnable() {
@Override
public void run() {
try {
if ((int) did == 0 || messagesOnly == 2) {
SQLiteCursor cursor = database.queryFinalized("SELECT data FROM messages WHERE uid = " + did);
ArrayList<File> filesToDelete = new ArrayList<>();
try {
while (cursor.next()) {
NativeByteBuffer data = new NativeByteBuffer(cursor.byteArrayLength(0));
if (data != null && cursor.byteBufferValue(0, data) != 0) {
TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false);
if (message != null && message.media != null) {
if (message.media instanceof TLRPC.TL_messageMediaPhoto) {
for (TLRPC.PhotoSize photoSize : message.media.photo.sizes) {
File file = FileLoader.getPathToAttach(photoSize);
if (file != null && file.toString().length() > 0) {
filesToDelete.add(file);
}
}
} else if (message.media instanceof TLRPC.TL_messageMediaDocument) {
File file = FileLoader.getPathToAttach(message.media.document);
if (file != null && file.toString().length() > 0) {
filesToDelete.add(file);
}
file = FileLoader.getPathToAttach(message.media.document.thumb);
if (file != null && file.toString().length() > 0) {
filesToDelete.add(file);
}
}
}
}
data.reuse();
}
} catch (Exception e) {
@ -1058,6 +1138,7 @@ public class MessagesStorage {
if (messagesOnly == 0) {
database.executeFast("DELETE FROM dialogs WHERE did = " + did).stepThis().dispose();
database.executeFast("DELETE FROM chat_settings_v2 WHERE uid = " + did).stepThis().dispose();
database.executeFast("DELETE FROM chat_pinned WHERE uid = " + did).stepThis().dispose();
database.executeFast("DELETE FROM channel_users_v2 WHERE did = " + did).stepThis().dispose();
database.executeFast("DELETE FROM search_recent WHERE did = " + did).stepThis().dispose();
int lower_id = (int)did;
@ -1084,10 +1165,9 @@ public class MessagesStorage {
NativeByteBuffer data = new NativeByteBuffer(cursor2.byteArrayLength(0));
if (data != null && cursor2.byteBufferValue(0, data) != 0) {
TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false);
if (message == null) {
continue;
if (message != null) {
arrayList.add(message);
}
arrayList.add(message);
}
data.reuse();
}
@ -1429,13 +1509,14 @@ public class MessagesStorage {
@Override
public void run() {
try {
SQLiteCursor cursor = database.queryFinalized("SELECT info FROM chat_settings_v2 WHERE uid = " + participants.chat_id);
SQLiteCursor cursor = database.queryFinalized("SELECT info, pinned FROM chat_settings_v2 WHERE uid = " + participants.chat_id);
TLRPC.ChatFull info = null;
ArrayList<TLRPC.User> loadedUsers = new ArrayList<>();
if (cursor.next()) {
NativeByteBuffer data = new NativeByteBuffer(cursor.byteArrayLength(0));
if (data != null && cursor.byteBufferValue(0, data) != 0) {
info = TLRPC.ChatFull.TLdeserialize(data, data.readInt32(false), false);
info.pinned_msg_id = cursor.intValue(1);
}
data.reuse();
}
@ -1446,15 +1527,16 @@ public class MessagesStorage {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.chatInfoDidLoaded, finalInfo, 0, false);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.chatInfoDidLoaded, finalInfo, 0, false, null);
}
});
SQLitePreparedStatement state = database.executeFast("REPLACE INTO chat_settings_v2 VALUES(?, ?)");
SQLitePreparedStatement state = database.executeFast("REPLACE INTO chat_settings_v2 VALUES(?, ?, ?)");
NativeByteBuffer data = new NativeByteBuffer(info.getObjectSize());
info.serializeToStream(data);
state.bindInteger(1, info.id);
state.bindByteBuffer(2, data);
state.bindInteger(3, info.pinned_msg_id);
state.step();
state.dispose();
data.reuse();
@ -1516,11 +1598,12 @@ public class MessagesStorage {
return;
}
}
SQLitePreparedStatement state = database.executeFast("REPLACE INTO chat_settings_v2 VALUES(?, ?)");
SQLitePreparedStatement state = database.executeFast("REPLACE INTO chat_settings_v2 VALUES(?, ?, ?)");
NativeByteBuffer data = new NativeByteBuffer(info.getObjectSize());
info.serializeToStream(data);
state.bindInteger(1, info.id);
state.bindByteBuffer(2, data);
state.bindInteger(3, info.pinned_msg_id);
state.step();
state.dispose();
data.reuse();
@ -1557,18 +1640,65 @@ public class MessagesStorage {
});
}
public void updateChannelPinnedMessage(final int channelId, final int messageId) {
storageQueue.postRunnable(new Runnable() {
@Override
public void run() {
try {
SQLiteCursor cursor = database.queryFinalized("SELECT info, pinned FROM chat_settings_v2 WHERE uid = " + channelId);
TLRPC.ChatFull info = null;
ArrayList<TLRPC.User> loadedUsers = new ArrayList<>();
if (cursor.next()) {
NativeByteBuffer data = new NativeByteBuffer(cursor.byteArrayLength(0));
if (cursor.byteBufferValue(0, data) != 0) {
info = TLRPC.ChatFull.TLdeserialize(data, data.readInt32(false), false);
info.pinned_msg_id = cursor.intValue(1);
}
data.reuse();
}
cursor.dispose();
if (info instanceof TLRPC.TL_channelFull) {
info.pinned_msg_id = messageId;
info.flags |= 32;
final TLRPC.ChatFull finalInfo = info;
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.chatInfoDidLoaded, finalInfo, 0, false, null);
}
});
SQLitePreparedStatement state = database.executeFast("REPLACE INTO chat_settings_v2 VALUES(?, ?, ?)");
NativeByteBuffer data = new NativeByteBuffer(info.getObjectSize());
info.serializeToStream(data);
state.bindInteger(1, channelId);
state.bindByteBuffer(2, data);
state.bindInteger(3, info.pinned_msg_id);
state.step();
state.dispose();
data.reuse();
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
});
}
public void updateChatInfo(final int chat_id, final int user_id, final int what, final int invited_id, final int version) {
storageQueue.postRunnable(new Runnable() {
@Override
public void run() {
try {
SQLiteCursor cursor = database.queryFinalized("SELECT info FROM chat_settings_v2 WHERE uid = " + chat_id);
SQLiteCursor cursor = database.queryFinalized("SELECT info, pinned FROM chat_settings_v2 WHERE uid = " + chat_id);
TLRPC.ChatFull info = null;
ArrayList<TLRPC.User> loadedUsers = new ArrayList<>();
if (cursor.next()) {
NativeByteBuffer data = new NativeByteBuffer(cursor.byteArrayLength(0));
if (data != null && cursor.byteBufferValue(0, data) != 0) {
info = TLRPC.ChatFull.TLdeserialize(data, data.readInt32(false), false);
info.pinned_msg_id = cursor.intValue(1);
}
data.reuse();
}
@ -1620,15 +1750,16 @@ public class MessagesStorage {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.chatInfoDidLoaded, finalInfo, 0, false);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.chatInfoDidLoaded, finalInfo, 0, false, null);
}
});
SQLitePreparedStatement state = database.executeFast("REPLACE INTO chat_settings_v2 VALUES(?, ?)");
SQLitePreparedStatement state = database.executeFast("REPLACE INTO chat_settings_v2 VALUES(?, ?, ?)");
NativeByteBuffer data = new NativeByteBuffer(info.getObjectSize());
info.serializeToStream(data);
state.bindInteger(1, chat_id);
state.bindByteBuffer(2, data);
state.bindInteger(3, info.pinned_msg_id);
state.step();
state.dispose();
data.reuse();
@ -1684,13 +1815,14 @@ public class MessagesStorage {
@Override
public void run() {
try {
SQLiteCursor cursor = database.queryFinalized("SELECT info FROM chat_settings_v2 WHERE uid = " + chat_id);
SQLiteCursor cursor = database.queryFinalized("SELECT info, pinned FROM chat_settings_v2 WHERE uid = " + chat_id);
TLRPC.ChatFull info = null;
ArrayList<TLRPC.User> loadedUsers = new ArrayList<>();
if (cursor.next()) {
NativeByteBuffer data = new NativeByteBuffer(cursor.byteArrayLength(0));
if (data != null && cursor.byteBufferValue(0, data) != 0) {
info = TLRPC.ChatFull.TLdeserialize(data, data.readInt32(false), false);
info.pinned_msg_id = cursor.intValue(1);
}
data.reuse();
}
@ -1754,7 +1886,12 @@ public class MessagesStorage {
if (semaphore != null) {
semaphore.release();
}
MessagesController.getInstance().processChatInfo(chat_id, info, loadedUsers, true, force, byChannelUsers);
MessageObject pinnedMessageObject = null;
if (info instanceof TLRPC.TL_channelFull && info.pinned_msg_id != 0) {
pinnedMessageObject = MessagesQuery.loadPinnedMessage(chat_id, info.pinned_msg_id, false);
}
MessagesController.getInstance().processChatInfo(chat_id, info, loadedUsers, true, force, byChannelUsers, pinnedMessageObject);
} catch (Exception e) {
FileLog.e("tmessages", e);
} finally {
@ -2281,7 +2418,7 @@ public class MessagesStorage {
holeMessageMinId |= ((long) channelId) << 32;
}
}
/*if (holeMessageMaxId == holeMessageMinId) { TODO ???
/*if (holeMessageMaxId == holeMessageMinId) {
holeMessageMaxId = 0;
holeMessageMinId = 1;
}*/
@ -2422,19 +2559,17 @@ public class MessagesStorage {
addUsersAndChatsFromMessage(message, usersToLoad, chatsToLoad);
if (message.reply_to_msg_id != 0 || message.reply_to_random_id != 0) {
boolean ok = false;
if (!cursor.isNull(6)) {
NativeByteBuffer data2 = new NativeByteBuffer(cursor.byteArrayLength(6));
if (data2 != null && cursor.byteBufferValue(6, data2) != 0) {
message.replyMessage = TLRPC.Message.TLdeserialize(data2, data2.readInt32(false), false);
if (message.replyMessage != null) {
addUsersAndChatsFromMessage(message.replyMessage, usersToLoad, chatsToLoad);
ok = true;
}
}
data2.reuse();
}
if (!ok) {
if (message.replyMessage == null) {
if (message.reply_to_msg_id != 0) {
long messageId = message.reply_to_msg_id;
if (message.to_id.channel_id != 0) {
@ -3037,7 +3172,7 @@ public class MessagesStorage {
NativeByteBuffer data = new NativeByteBuffer(cursor.byteArrayLength(0));
if (data != null && cursor.byteBufferValue(0, data) != 0) {
TLRPC.User oldUser = TLRPC.User.TLdeserialize(data, data.readInt32(false), false);
if (user != null) {
if (oldUser != null) {
if (user.first_name != null) {
oldUser.first_name = user.first_name;
oldUser.flags |= 2;
@ -3052,6 +3187,13 @@ public class MessagesStorage {
oldUser.last_name = null;
oldUser.flags = oldUser.flags &~ 4;
}
if (user.username != null) {
oldUser.username = user.username;
oldUser.flags |= 8;
} else {
oldUser.username = null;
oldUser.flags = oldUser.flags &~ 8;
}
if (user.photo != null) {
oldUser.photo = user.photo;
oldUser.flags |= 32;
@ -3059,8 +3201,8 @@ public class MessagesStorage {
oldUser.photo = null;
oldUser.flags = oldUser.flags &~ 32;
}
user = oldUser;
}
user = oldUser;
}
data.reuse();
} catch (Exception e) {
@ -3099,6 +3241,36 @@ public class MessagesStorage {
SQLitePreparedStatement state = database.executeFast("REPLACE INTO chats VALUES(?, ?, ?)");
for (int a = 0; a < chats.size(); a++) {
TLRPC.Chat chat = chats.get(a);
if (chat.min) {
SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT data FROM chats WHERE uid = %d", chat.id));
if (cursor.next()) {
try {
NativeByteBuffer data = new NativeByteBuffer(cursor.byteArrayLength(0));
if (data != null && cursor.byteBufferValue(0, data) != 0) {
TLRPC.Chat oldChat = TLRPC.Chat.TLdeserialize(data, data.readInt32(false), false);
if (oldChat != null) {
oldChat.title = chat.title;
oldChat.photo = chat.photo;
oldChat.broadcast = chat.broadcast;
oldChat.verified = chat.verified;
oldChat.megagroup = chat.megagroup;
oldChat.democracy = chat.democracy;
if (chat.username != null) {
oldChat.username = chat.username;
oldChat.flags |= 64;
} else {
oldChat.username = null;
oldChat.flags = oldChat.flags &~ 64;
}
chat = oldChat;
}
}
data.reuse();
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
}
state.requery();
NativeByteBuffer data = new NativeByteBuffer(chat.getObjectSize());
chat.serializeToStream(data);
@ -4119,7 +4291,6 @@ public class MessagesStorage {
} catch (Exception e2) {
FileLog.e("tmessages", e2);
}
FileLog.e("tmessages", e);
} finally {
if (state != null) {
state.dispose();
@ -4138,7 +4309,6 @@ public class MessagesStorage {
} catch (Exception e2) {
FileLog.e("tmessages", e2);
}
FileLog.e("tmessages", e);
} finally {
if (state != null) {
state.dispose();
@ -4388,24 +4558,23 @@ public class MessagesStorage {
NativeByteBuffer data = new NativeByteBuffer(cursor.byteArrayLength(1));
if (data != null && cursor.byteBufferValue(1, data) != 0) {
TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false);
if (message == null || message.media == null) {
continue;
}
if (message.media instanceof TLRPC.TL_messageMediaPhoto) {
for (TLRPC.PhotoSize photoSize : message.media.photo.sizes) {
File file = FileLoader.getPathToAttach(photoSize);
if (message != null && message.media != null) {
if (message.media instanceof TLRPC.TL_messageMediaPhoto) {
for (TLRPC.PhotoSize photoSize : message.media.photo.sizes) {
File file = FileLoader.getPathToAttach(photoSize);
if (file != null && file.toString().length() > 0) {
filesToDelete.add(file);
}
}
} else if (message.media instanceof TLRPC.TL_messageMediaDocument) {
File file = FileLoader.getPathToAttach(message.media.document);
if (file != null && file.toString().length() > 0) {
filesToDelete.add(file);
}
file = FileLoader.getPathToAttach(message.media.document.thumb);
if (file != null && file.toString().length() > 0) {
filesToDelete.add(file);
}
}
} else if (message.media instanceof TLRPC.TL_messageMediaDocument) {
File file = FileLoader.getPathToAttach(message.media.document);
if (file != null && file.toString().length() > 0) {
filesToDelete.add(file);
}
file = FileLoader.getPathToAttach(message.media.document.thumb);
if (file != null && file.toString().length() > 0) {
filesToDelete.add(file);
}
}
}
@ -5136,7 +5305,9 @@ public class MessagesStorage {
usersToLoad.add(UserConfig.getClientUserId());
ArrayList<Integer> chatsToLoad = new ArrayList<>();
ArrayList<Integer> encryptedToLoad = new ArrayList<>();
SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT d.did, d.last_mid, d.unread_count, d.date, m.data, m.read_state, m.mid, m.send_state, s.flags, m.date, d.last_mid_i, d.unread_count_i, d.pts, d.inbox_max, d.date_i FROM dialogs as d LEFT JOIN messages as m ON d.last_mid = m.mid LEFT JOIN dialog_settings as s ON d.did = s.did ORDER BY d.date DESC LIMIT %d,%d", offset, count));
ArrayList<Long> replyMessages = new ArrayList<>();
HashMap<Long, TLRPC.Message> replyMessageOwners = new HashMap<>();
SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT d.did, d.last_mid, d.unread_count, d.date, m.data, m.read_state, m.mid, m.send_state, s.flags, m.date, d.last_mid_i, d.unread_count_i, d.pts, d.inbox_max, d.date_i, m.replydata FROM dialogs as d LEFT JOIN messages as m ON d.last_mid = m.mid LEFT JOIN dialog_settings as s ON d.did = s.did ORDER BY d.date DESC LIMIT %d,%d", offset, count));
while (cursor.next()) {
TLRPC.Dialog dialog;
int pts = cursor.intValue(12);
@ -5181,6 +5352,33 @@ public class MessagesStorage {
dialogs.messages.add(message);
addUsersAndChatsFromMessage(message, usersToLoad, chatsToLoad);
try {
if (message.reply_to_msg_id != 0 && message.action instanceof TLRPC.TL_messageActionPinMessage) {
if (!cursor.isNull(15)) {
NativeByteBuffer data2 = new NativeByteBuffer(cursor.byteArrayLength(15));
if (cursor.byteBufferValue(15, data2) != 0) {
message.replyMessage = TLRPC.Message.TLdeserialize(data2, data2.readInt32(false), false);
if (message.replyMessage != null) {
addUsersAndChatsFromMessage(message.replyMessage, usersToLoad, chatsToLoad);
}
}
data2.reuse();
}
if (message.replyMessage == null) {
long messageId = message.reply_to_msg_id;
if (message.to_id.channel_id != 0) {
messageId |= ((long) message.to_id.channel_id) << 32;
}
if (!replyMessages.contains(messageId)) {
replyMessages.add(messageId);
}
replyMessageOwners.put(dialog.id, message);
}
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
}
data.reuse();
@ -5211,6 +5409,29 @@ public class MessagesStorage {
}
cursor.dispose();
if (!replyMessages.isEmpty()) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid, date, uid FROM messages WHERE mid IN(%s)", TextUtils.join(",", replyMessages)));
while (cursor.next()) {
NativeByteBuffer data = new NativeByteBuffer(cursor.byteArrayLength(0));
if (data != null && cursor.byteBufferValue(0, data) != 0) {
TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false);
message.id = cursor.intValue(1);
message.date = cursor.intValue(2);
message.dialog_id = cursor.longValue(3);
addUsersAndChatsFromMessage(message, usersToLoad, chatsToLoad);
TLRPC.Message owner = replyMessageOwners.get(message.dialog_id);
if (owner != null) {
owner.replyMessage = message;
message.dialog_id = owner.dialog_id;
}
}
data.reuse();
}
cursor.dispose();
}
if (!encryptedToLoad.isEmpty()) {
getEncryptedChatsInternal(TextUtils.join(",", encryptedToLoad), encryptedChats, usersToLoad);
}

View File

@ -23,7 +23,7 @@ import java.util.zip.ZipFile;
public class NativeLoader {
private final static int LIB_VERSION = 19;
private final static int LIB_VERSION = 20;
private final static String LIB_NAME = "tmessages." + LIB_VERSION;
private final static String LIB_SO_NAME = "lib" + LIB_NAME + ".so";
private final static String LOCALE_LIB_SO_NAME = "lib" + LIB_NAME + "loc.so";

View File

@ -53,6 +53,7 @@ public class NotificationCenter {
public static final int didSetTwoStepPassword = totalEvents++;
public static final int screenStateChanged = totalEvents++;
public static final int didLoadedReplyMessages = totalEvents++;
public static final int didLoadedPinnedMessage = totalEvents++;
public static final int newSessionReceived = totalEvents++;
public static final int didReceivedWebpages = totalEvents++;
public static final int didReceivedWebpagesInUpdates = totalEvents++;
@ -60,6 +61,7 @@ public class NotificationCenter {
public static final int didReplacedPhotoInMemCache = totalEvents++;
public static final int messagesReadContent = totalEvents++;
public static final int botInfoDidLoaded = totalEvents++;
public static final int userInfoDidLoaded = totalEvents++;
public static final int botKeyboardDidLoaded = totalEvents++;
public static final int chatSearchResultsAvailable = totalEvents++;
public static final int musicDidLoaded = totalEvents++;
@ -67,6 +69,7 @@ public class NotificationCenter {
public static final int didUpdatedMessagesViews = totalEvents++;
public static final int needReloadRecentDialogsSearch = totalEvents++;
public static final int locationPermissionGranted = totalEvents++;
public static final int peerSettingsDidLoaded = totalEvents++;
public static final int httpFileDidLoaded = totalEvents++;
public static final int httpFileDidFailedLoad = totalEvents++;

View File

@ -61,7 +61,7 @@ public class NotificationsController {
private int wearNotificationId = 10000;
private int autoNotificationId = 20000;
public ArrayList<MessageObject> popupMessages = new ArrayList<>();
private long openned_dialog_id = 0;
private long opened_dialog_id = 0;
private int total_unread_count = 0;
private int personal_count = 0;
private boolean notifyCheck = false;
@ -147,7 +147,7 @@ public class NotificationsController {
notificationsQueue.postRunnable(new Runnable() {
@Override
public void run() {
openned_dialog_id = 0;
opened_dialog_id = 0;
total_unread_count = 0;
personal_count = 0;
pushMessages.clear();
@ -178,11 +178,11 @@ public class NotificationsController {
inChatSoundEnabled = value;
}
public void setOpennedDialogId(final long dialog_id) {
public void setOpenedDialogId(final long dialog_id) {
notificationsQueue.postRunnable(new Runnable() {
@Override
public void run() {
openned_dialog_id = dialog_id;
opened_dialog_id = dialog_id;
}
});
}
@ -393,7 +393,7 @@ public class NotificationsController {
}
long dialog_id = messageObject.getDialogId();
long original_dialog_id = dialog_id;
if (dialog_id == openned_dialog_id && ApplicationLoader.isScreenOn) {
if (dialog_id == opened_dialog_id && ApplicationLoader.isScreenOn) {
playInChatSound();
continue;
}
@ -406,7 +406,7 @@ public class NotificationsController {
added = true;
Boolean value = settingsCache.get(dialog_id);
boolean isChat = (int)dialog_id < 0;
boolean isChat = (int) dialog_id < 0;
popup = (int)dialog_id == 0 ? 0 : preferences.getInt(isChat ? "popupGroup" : "popupAll", 0);
if (value == null) {
int notifyOverride = getNotifyOverride(preferences, dialog_id);
@ -582,7 +582,7 @@ public class NotificationsController {
value = !(notifyOverride == 2 || (!preferences.getBoolean("EnableAll", true) || ((int) dialog_id < 0) && !preferences.getBoolean("EnableGroup", true)) && notifyOverride == 0);
settingsCache.put(dialog_id, value);
}
if (!value || dialog_id == openned_dialog_id && ApplicationLoader.isScreenOn) {
if (!value || dialog_id == opened_dialog_id && ApplicationLoader.isScreenOn) {
continue;
}
pushMessagesDict.put(mid, messageObject);
@ -757,6 +757,8 @@ public class NotificationsController {
msg = LocaleController.formatString("NotificationMessageVideo", R.string.NotificationMessageVideo, name);
} else if (messageObject.isVoice()) {
msg = LocaleController.formatString("NotificationMessageAudio", R.string.NotificationMessageAudio, name);
} else if (messageObject.isMusic()) {
msg = LocaleController.formatString("NotificationMessageMusic", R.string.NotificationMessageMusic, name);
} else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaContact) {
msg = LocaleController.formatString("NotificationMessageContact", R.string.NotificationMessageContact, name);
} else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeo || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVenue) {
@ -801,7 +803,11 @@ public class NotificationsController {
return null;
}
if (from_id == u2.id) {
msg = LocaleController.formatString("NotificationGroupAddSelf", R.string.NotificationGroupAddSelf, name, chat.title);
if (messageObject.isMegagroup()) {
msg = LocaleController.formatString("NotificationGroupAddSelfMega", R.string.NotificationGroupAddSelfMega, name, chat.title);
} else {
msg = LocaleController.formatString("NotificationGroupAddSelf", R.string.NotificationGroupAddSelf, name, chat.title);
}
} else {
msg = LocaleController.formatString("NotificationGroupAddMember", R.string.NotificationGroupAddMember, name, chat.title, UserObject.getUserName(u2));
}
@ -851,10 +857,91 @@ public class NotificationsController {
msg = LocaleController.formatString("ActionMigrateFromGroupNotify", R.string.ActionMigrateFromGroupNotify, chat.title);
} else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionChannelMigrateFrom) {
msg = LocaleController.formatString("ActionMigrateFromGroupNotify", R.string.ActionMigrateFromGroupNotify, messageObject.messageOwner.action.title);
} else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionPinMessage) {
if (messageObject.replyMessageObject == null) {
if (!ChatObject.isChannel(chat) || chat.megagroup) {
msg = LocaleController.formatString("NotificationActionPinnedNoText", R.string.NotificationActionPinnedNoText, name, chat.title);
} else {
msg = LocaleController.formatString("NotificationActionPinnedNoTextChannel", R.string.NotificationActionPinnedNoTextChannel, name, chat.title);
}
} else {
MessageObject object = messageObject.replyMessageObject;
if (object.isMusic()) {
if (!ChatObject.isChannel(chat) || chat.megagroup) {
msg = LocaleController.formatString("NotificationActionPinnedMusic", R.string.NotificationActionPinnedMusic, name, chat.title);
} else {
msg = LocaleController.formatString("NotificationActionPinnedMusicChannel", R.string.NotificationActionPinnedMusicChannel, chat.title);
}
} else if (object.isVideo()) {
if (!ChatObject.isChannel(chat) || chat.megagroup) {
msg = LocaleController.formatString("NotificationActionPinnedVideo", R.string.NotificationActionPinnedVideo, name, chat.title);
} else {
msg = LocaleController.formatString("NotificationActionPinnedVideoChannel", R.string.NotificationActionPinnedVideoChannel, chat.title);
}
} else if (object.isGif()) {
if (!ChatObject.isChannel(chat) || chat.megagroup) {
msg = LocaleController.formatString("NotificationActionPinnedGif", R.string.NotificationActionPinnedGif, name, chat.title);
} else {
msg = LocaleController.formatString("NotificationActionPinnedGifChannel", R.string.NotificationActionPinnedGifChannel, chat.title);
}
} else if (object.isVoice()) {
if (!ChatObject.isChannel(chat) || chat.megagroup) {
msg = LocaleController.formatString("NotificationActionPinnedVoice", R.string.NotificationActionPinnedVoice, name, chat.title);
} else {
msg = LocaleController.formatString("NotificationActionPinnedVoiceChannel", R.string.NotificationActionPinnedVoiceChannel, chat.title);
}
} else if (object.isSticker()) {
if (!ChatObject.isChannel(chat) || chat.megagroup) {
msg = LocaleController.formatString("NotificationActionPinnedSticker", R.string.NotificationActionPinnedSticker, name, chat.title);
} else {
msg = LocaleController.formatString("NotificationActionPinnedStickerChannel", R.string.NotificationActionPinnedStickerChannel, chat.title);
}
} else if (object.messageOwner.media instanceof TLRPC.TL_messageMediaDocument) {
if (!ChatObject.isChannel(chat) || chat.megagroup) {
msg = LocaleController.formatString("NotificationActionPinnedFile", R.string.NotificationActionPinnedFile, name, chat.title);
} else {
msg = LocaleController.formatString("NotificationActionPinnedFileChannel", R.string.NotificationActionPinnedFileChannel, chat.title);
}
} else if (object.messageOwner.media instanceof TLRPC.TL_messageMediaGeo) {
if (!ChatObject.isChannel(chat) || chat.megagroup) {
msg = LocaleController.formatString("NotificationActionPinnedGeo", R.string.NotificationActionPinnedGeo, name, chat.title);
} else {
msg = LocaleController.formatString("NotificationActionPinnedGeoChannel", R.string.NotificationActionPinnedGeoChannel, chat.title);
}
} else if (object.messageOwner.media instanceof TLRPC.TL_messageMediaContact) {
if (!ChatObject.isChannel(chat) || chat.megagroup) {
msg = LocaleController.formatString("NotificationActionPinnedContact", R.string.NotificationActionPinnedContact, name, chat.title);
} else {
msg = LocaleController.formatString("NotificationActionPinnedContactChannel", R.string.NotificationActionPinnedContactChannel, chat.title);
}
} else if (object.messageOwner.media instanceof TLRPC.TL_messageMediaPhoto) {
if (!ChatObject.isChannel(chat) || chat.megagroup) {
msg = LocaleController.formatString("NotificationActionPinnedPhoto", R.string.NotificationActionPinnedPhoto, name, chat.title);
} else {
msg = LocaleController.formatString("NotificationActionPinnedPhotoChannel", R.string.NotificationActionPinnedPhotoChannel, chat.title);
}
} else if (object.messageText != null && object.messageText.length() > 0) {
CharSequence message = object.messageText;
if (message.length() > 20) {
message = message.subSequence(0, 20) + "...";
}
if (!ChatObject.isChannel(chat) || chat.megagroup) {
msg = LocaleController.formatString("NotificationActionPinnedText", R.string.NotificationActionPinnedText, name, message, chat.title);
} else {
msg = LocaleController.formatString("NotificationActionPinnedTextChannel", R.string.NotificationActionPinnedTextChannel, chat.title, message);
}
} else {
if (!ChatObject.isChannel(chat) || chat.megagroup) {
msg = LocaleController.formatString("NotificationActionPinnedNoText", R.string.NotificationActionPinnedNoText, name, chat.title);
} else {
msg = LocaleController.formatString("NotificationActionPinnedNoTextChannel", R.string.NotificationActionPinnedNoTextChannel, chat.title);
}
}
}
}
} else {
if (ChatObject.isChannel(chat) && !chat.megagroup) {
if (from_id < 0) {
if (messageObject.isImportant()) {
if (messageObject.isMediaEmpty()) {
if (!shortMessage && messageObject.messageOwner.message != null && messageObject.messageOwner.message.length() != 0) {
msg = LocaleController.formatString("NotificationMessageGroupText", R.string.NotificationMessageGroupText, name, chat.title, messageObject.messageOwner.message);
@ -867,6 +954,8 @@ public class NotificationsController {
msg = LocaleController.formatString("ChannelMessageVideo", R.string.ChannelMessageVideo, name, chat.title);
} else if (messageObject.isVoice()) {
msg = LocaleController.formatString("ChannelMessageAudio", R.string.ChannelMessageAudio, name, chat.title);
} else if (messageObject.isMusic()) {
msg = LocaleController.formatString("ChannelMessageMusic", R.string.ChannelMessageMusic, name, chat.title);
} else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaContact) {
msg = LocaleController.formatString("ChannelMessageContact", R.string.ChannelMessageContact, name, chat.title);
} else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeo || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVenue) {
@ -893,6 +982,8 @@ public class NotificationsController {
msg = LocaleController.formatString("ChannelMessageGroupVideo", R.string.ChannelMessageGroupVideo, name, chat.title);
} else if (messageObject.isVoice()) {
msg = LocaleController.formatString("ChannelMessageGroupAudio", R.string.ChannelMessageGroupAudio, name, chat.title);
} else if (messageObject.isMusic()) {
msg = LocaleController.formatString("ChannelMessageGroupMusic", R.string.ChannelMessageGroupMusic, name, chat.title);
} else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaContact) {
msg = LocaleController.formatString("ChannelMessageGroupContact", R.string.ChannelMessageGroupContact, name, chat.title);
} else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeo || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVenue) {
@ -920,6 +1011,8 @@ public class NotificationsController {
msg = LocaleController.formatString("NotificationMessageGroupVideo", R.string.NotificationMessageGroupVideo, name, chat.title);
} else if (messageObject.isVoice()) {
msg = LocaleController.formatString("NotificationMessageGroupAudio", R.string.NotificationMessageGroupAudio, name, chat.title);
} else if (messageObject.isMusic()) {
msg = LocaleController.formatString("NotificationMessageGroupMusic", R.string.NotificationMessageGroupMusic, name, chat.title);
} else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaContact) {
msg = LocaleController.formatString("NotificationMessageGroupContact", R.string.NotificationMessageGroupContact, name, chat.title);
} else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeo || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVenue) {
@ -1073,7 +1166,7 @@ public class NotificationsController {
try {
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Context.MODE_PRIVATE);
int notifyOverride = getNotifyOverride(preferences, openned_dialog_id);
int notifyOverride = getNotifyOverride(preferences, opened_dialog_id);
if (notifyOverride == 2) {
return;
}
@ -1405,12 +1498,6 @@ public class NotificationsController {
}
}
if (silent == 1) {
FileLog.e("tmessages", "don't notify " + lastMessage);
} else {
FileLog.e("tmessages", "notify" + lastMessage);
}
if (!notifyAboutLast || silent == 1) {
mBuilder.setPriority(NotificationCompat.PRIORITY_LOW);
} else {

View File

@ -820,6 +820,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
req.message = message;
req.id = messageObject.getId();
req.no_webpage = !searchLinks;
FileLog.d("tmessages", "try to edit message " + req.id + " in channel " + req.channel.channel_id + " hash " + req.channel.access_hash + " message " + req.message);
final int reqId = ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
@Override
public void run(TLObject response, TLRPC.TL_error error) {
@ -2002,6 +2003,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
if (!isSentError) {
newMsgObj.send_state = MessageObject.MESSAGE_SEND_STATE_SENT;
NotificationCenter.getInstance().postNotificationName(NotificationCenter.messageReceivedByServer, oldId, (isBroadcast ? oldId : newMsgObj.id), newMsgObj, newMsgObj.dialog_id); //TODO remove later?
MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() {
@Override
public void run() {
@ -2274,7 +2276,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
if (extension == null) {
extension = "txt";
}
path = MediaController.copyDocumentToCache(uri, extension);
path = MediaController.copyFileToCache(uri, extension);
if (path == null) {
return false;
}
@ -2923,12 +2925,12 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
if (MediaController.isGif(uri)) {
isDocument = true;
originalPath = uri.toString();
tempPath = MediaController.copyDocumentToCache(uri, "gif");
tempPath = MediaController.copyFileToCache(uri, "gif");
extension = "gif";
} else if (MediaController.isWebp(uri)) {
isDocument = true;
originalPath = uri.toString();
tempPath = MediaController.copyDocumentToCache(uri, "webp");
tempPath = MediaController.copyFileToCache(uri, "webp");
extension = "webp";
}
}
@ -3009,8 +3011,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
}
TLRPC.TL_document document = null;
if (!isEncrypted) {
TLObject object = MessagesStorage.getInstance().getSentFile(originalPath, !isEncrypted ? 2 : 5);
document = (TLRPC.TL_document) object;
//document = (TLRPC.TL_document) MessagesStorage.getInstance().getSentFile(originalPath, !isEncrypted ? 2 : 5);
}
if (document == null) {
Bitmap thumb = ThumbnailUtils.createVideoThumbnail(videoPath, MediaStore.Video.Thumbnails.MINI_KIND);

View File

@ -9,8 +9,10 @@
package org.telegram.messenger;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ComponentName;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
@ -48,6 +50,11 @@ public class TgChooserTargetService extends ChooserTargetService {
if (!UserConfig.isClientActivated()) {
return targets;
}
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
if (!preferences.getBoolean("direct_share", true)) {
return targets;
}
ImageLoader imageLoader = ImageLoader.getInstance();
final Semaphore semaphore = new Semaphore(0);
final ComponentName componentName = new ComponentName(getPackageName(), LaunchActivity.class.getCanonicalName());

View File

@ -179,7 +179,7 @@ public class BotQuery {
}
public static void putBotInfo(final TLRPC.BotInfo botInfo) {
if (botInfo == null || botInfo instanceof TLRPC.TL_botInfoEmpty) {
if (botInfo == null) {
return;
}
botInfos.put(botInfo.user_id, botInfo);

View File

@ -29,10 +29,158 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
public class ReplyMessageQuery {
public class MessagesQuery {
public static void loadReplyMessagesForMessages(final ArrayList<MessageObject> messages, final long dialog_id) {
if ((int) dialog_id == 0) {
public static MessageObject loadPinnedMessage(final int channelId, final int mid, boolean useQueue) {
if (useQueue) {
MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() {
@Override
public void run() {
loadPinnedMessageInternal(channelId, mid, false);
}
});
} else {
return loadPinnedMessageInternal(channelId, mid, true);
}
return null;
}
private static MessageObject loadPinnedMessageInternal(final int channelId, final int mid, boolean returnValue) {
try {
long messageId = ((long) mid) | ((long) channelId) << 32;
TLRPC.Message result = null;
final ArrayList<TLRPC.User> users = new ArrayList<>();
final ArrayList<TLRPC.Chat> chats = new ArrayList<>();
ArrayList<Integer> usersToLoad = new ArrayList<>();
ArrayList<Integer> chatsToLoad = new ArrayList<>();
SQLiteCursor cursor = MessagesStorage.getInstance().getDatabase().queryFinalized(String.format(Locale.US, "SELECT data, mid, date FROM messages WHERE mid = %d", messageId));
if (cursor.next()) {
NativeByteBuffer data = new NativeByteBuffer(cursor.byteArrayLength(0));
if (data != null && cursor.byteBufferValue(0, data) != 0) {
result = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false);
result.id = cursor.intValue(1);
result.date = cursor.intValue(2);
result.dialog_id = -channelId;
MessagesStorage.addUsersAndChatsFromMessage(result, usersToLoad, chatsToLoad);
}
data.reuse();
}
cursor.dispose();
if (result == null) {
cursor = MessagesStorage.getInstance().getDatabase().queryFinalized(String.format(Locale.US, "SELECT data FROM chat_pinned WHERE uid = %d", channelId));
if (cursor.next()) {
NativeByteBuffer data = new NativeByteBuffer(cursor.byteArrayLength(0));
if (data != null && cursor.byteBufferValue(0, data) != 0) {
result = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false);
if (result.id != mid) {
result = null;
} else {
result.dialog_id = -channelId;
MessagesStorage.addUsersAndChatsFromMessage(result, usersToLoad, chatsToLoad);
}
}
data.reuse();
}
cursor.dispose();
}
if (result == null) {
final TLRPC.TL_channels_getMessages req = new TLRPC.TL_channels_getMessages();
req.channel = MessagesController.getInputChannel(channelId);
req.id.add(mid);
ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
@Override
public void run(TLObject response, TLRPC.TL_error error) {
boolean ok = false;
if (error == null) {
TLRPC.messages_Messages messagesRes = (TLRPC.messages_Messages) response;
if (!messagesRes.messages.isEmpty()) {
ImageLoader.saveMessagesThumbs(messagesRes.messages);
broadcastPinnedMessage(messagesRes.messages.get(0), messagesRes.users, messagesRes.chats, false, false);
MessagesStorage.getInstance().putUsersAndChats(messagesRes.users, messagesRes.chats, true, true);
savePinnedMessage(messagesRes.messages.get(0));
ok = true;
}
}
if (!ok) {
MessagesStorage.getInstance().updateChannelPinnedMessage(channelId, 0);
}
}
});
} else {
if (returnValue) {
return broadcastPinnedMessage(result, users, chats, true, returnValue);
} else {
if (!usersToLoad.isEmpty()) {
MessagesStorage.getInstance().getUsersInternal(TextUtils.join(",", usersToLoad), users);
}
if (!chatsToLoad.isEmpty()) {
MessagesStorage.getInstance().getChatsInternal(TextUtils.join(",", chatsToLoad), chats);
}
broadcastPinnedMessage(result, users, chats, true, false);
}
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
return null;
}
private static void savePinnedMessage(final TLRPC.Message result) {
MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() {
@Override
public void run() {
try {
MessagesStorage.getInstance().getDatabase().beginTransaction();
SQLitePreparedStatement state = MessagesStorage.getInstance().getDatabase().executeFast("REPLACE INTO chat_pinned VALUES(?, ?, ?)");
NativeByteBuffer data = new NativeByteBuffer(result.getObjectSize());
result.serializeToStream(data);
state.requery();
state.bindInteger(1, result.to_id.channel_id);
state.bindInteger(2, result.id);
state.bindByteBuffer(3, data);
state.step();
data.reuse();
state.dispose();
MessagesStorage.getInstance().getDatabase().commitTransaction();
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
});
}
private static MessageObject broadcastPinnedMessage(final TLRPC.Message result, final ArrayList<TLRPC.User> users, final ArrayList<TLRPC.Chat> chats, final boolean isCache, boolean returnValue) {
final HashMap<Integer, TLRPC.User> usersDict = new HashMap<>();
for (int a = 0; a < users.size(); a++) {
TLRPC.User user = users.get(a);
usersDict.put(user.id, user);
}
final HashMap<Integer, TLRPC.Chat> chatsDict = new HashMap<>();
for (int a = 0; a < chats.size(); a++) {
TLRPC.Chat chat = chats.get(a);
chatsDict.put(chat.id, chat);
}
if (returnValue) {
return new MessageObject(result, usersDict, chatsDict, false);
} else {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
MessagesController.getInstance().putUsers(users, isCache);
MessagesController.getInstance().putChats(chats, isCache);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.didLoadedPinnedMessage, new MessageObject(result, usersDict, chatsDict, false));
}
});
}
return null;
}
public static void loadReplyMessagesForMessages(final ArrayList<MessageObject> messages, final long dialogId) {
if ((int) dialogId == 0) {
final ArrayList<Long> replyMessages = new ArrayList<>();
final HashMap<Long, ArrayList<MessageObject>> replyMessageRandomOwners = new HashMap<>();
final StringBuilder stringBuilder = new StringBuilder();
@ -70,7 +218,7 @@ public class ReplyMessageQuery {
TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false);
message.id = cursor.intValue(1);
message.date = cursor.intValue(2);
message.dialog_id = dialog_id;
message.dialog_id = dialogId;
ArrayList<MessageObject> arrayList = replyMessageRandomOwners.remove(cursor.longValue(3));
@ -97,7 +245,7 @@ public class ReplyMessageQuery {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.didLoadedReplyMessages, dialog_id);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.didLoadedReplyMessages, dialogId);
}
});
} catch (Exception e) {
@ -105,7 +253,6 @@ public class ReplyMessageQuery {
}
}
});
} else {
final ArrayList<Integer> replyMessages = new ArrayList<>();
final HashMap<Integer, ArrayList<MessageObject>> replyMessageOwners = new HashMap<>();
@ -157,7 +304,7 @@ public class ReplyMessageQuery {
TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false);
message.id = cursor.intValue(1);
message.date = cursor.intValue(2);
message.dialog_id = dialog_id;
message.dialog_id = dialogId;
MessagesStorage.addUsersAndChatsFromMessage(message, usersToLoad, chatsToLoad);
result.add(message);
replyMessages.remove((Integer) message.id);
@ -172,7 +319,7 @@ public class ReplyMessageQuery {
if (!chatsToLoad.isEmpty()) {
MessagesStorage.getInstance().getChatsInternal(TextUtils.join(",", chatsToLoad), chats);
}
broadcastReplyMessages(result, replyMessageOwners, users, chats, dialog_id, true);
broadcastReplyMessages(result, replyMessageOwners, users, chats, dialogId, true);
if (!replyMessages.isEmpty()) {
if (channelIdFinal != 0) {
@ -185,7 +332,7 @@ public class ReplyMessageQuery {
if (error == null) {
TLRPC.messages_Messages messagesRes = (TLRPC.messages_Messages) response;
ImageLoader.saveMessagesThumbs(messagesRes.messages);
broadcastReplyMessages(messagesRes.messages, replyMessageOwners, messagesRes.users, messagesRes.chats, dialog_id, false);
broadcastReplyMessages(messagesRes.messages, replyMessageOwners, messagesRes.users, messagesRes.chats, dialogId, false);
MessagesStorage.getInstance().putUsersAndChats(messagesRes.users, messagesRes.chats, true, true);
saveReplyMessages(replyMessageOwners, messagesRes.messages);
}
@ -200,7 +347,7 @@ public class ReplyMessageQuery {
if (error == null) {
TLRPC.messages_Messages messagesRes = (TLRPC.messages_Messages) response;
ImageLoader.saveMessagesThumbs(messagesRes.messages);
broadcastReplyMessages(messagesRes.messages, replyMessageOwners, messagesRes.users, messagesRes.chats, dialog_id, false);
broadcastReplyMessages(messagesRes.messages, replyMessageOwners, messagesRes.users, messagesRes.chats, dialogId, false);
MessagesStorage.getInstance().putUsersAndChats(messagesRes.users, messagesRes.chats, true, true);
saveReplyMessages(replyMessageOwners, messagesRes.messages);
}
@ -223,12 +370,14 @@ public class ReplyMessageQuery {
try {
MessagesStorage.getInstance().getDatabase().beginTransaction();
SQLitePreparedStatement state = MessagesStorage.getInstance().getDatabase().executeFast("UPDATE messages SET replydata = ? WHERE mid = ?");
for (TLRPC.Message message : result) {
for (int a = 0; a < result.size(); a++) {
TLRPC.Message message = result.get(a);
ArrayList<MessageObject> messageObjects = replyMessageOwners.get(message.id);
if (messageObjects != null) {
NativeByteBuffer data = new NativeByteBuffer(message.getObjectSize());
message.serializeToStream(data);
for (MessageObject messageObject : messageObjects) {
for (int b = 0; b < messageObjects.size(); b++) {
MessageObject messageObject = messageObjects.get(b);
state.requery();
long messageId = messageObject.getId();
if (messageObject.messageOwner.to_id.channel_id != 0) {
@ -275,6 +424,9 @@ public class ReplyMessageQuery {
for (int b = 0; b < arrayList.size(); b++) {
MessageObject m = arrayList.get(b);
m.replyMessageObject = messageObject;
if (m.messageOwner.action instanceof TLRPC.TL_messageActionPinMessage) {
m.generatePinMessageText(null, null);
}
}
changed = true;
}

View File

@ -22,9 +22,6 @@ import android.util.Log;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import org.telegram.messenger.support.util.ThreadUtil;
import org.telegram.messenger.support.util.TileList;
/**
* A utility class that supports asynchronous content loading.
* <p>
@ -42,7 +39,7 @@ import org.telegram.messenger.support.util.TileList;
* 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 org.telegram.messenger.support.widget.RecyclerView}, but it does
* This class is designed to work with {@link android.support.v7.widget.RecyclerView}, but it does
* not depend on it and can be used with other list views.
*
*/
@ -113,7 +110,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 org.telegram.messenger.support.widget.RecyclerView.OnScrollListener#onScrolled}).
* {@link android.support.v7.widget.RecyclerView.OnScrollListener#onScrolled}).
*/
public void onRangeChanged() {
if (isRefreshPending()) {

View File

@ -18,10 +18,11 @@ package org.telegram.messenger.support.util;
import android.os.Handler;
import android.os.Looper;
import android.support.v4.content.ParallelExecutorCompat;
import android.util.Log;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
class MessageThreadUtil<T> implements ThreadUtil<T> {
@ -83,7 +84,8 @@ class MessageThreadUtil<T> implements ThreadUtil<T> {
public BackgroundCallback<T> getBackgroundProxy(final BackgroundCallback<T> callback) {
return new BackgroundCallback<T>() {
final private MessageQueue mQueue = new MessageQueue();
final private Executor mExecutor = Executors.newSingleThreadExecutor();
final private Executor mExecutor = ParallelExecutorCompat.getParallelExecutor();
AtomicBoolean mBackgroundRunning = new AtomicBoolean(false);
private static final int REFRESH = 1;
private static final int UPDATE_RANGE = 2;
@ -114,42 +116,51 @@ class MessageThreadUtil<T> implements ThreadUtil<T> {
private void sendMessage(SyncQueueItem msg) {
mQueue.sendMessage(msg);
mExecutor.execute(mBackgroundRunnable);
maybeExecuteBackgroundRunnable();
}
private void sendMessageAtFrontOfQueue(SyncQueueItem msg) {
mQueue.sendMessageAtFrontOfQueue(msg);
mExecutor.execute(mBackgroundRunnable);
maybeExecuteBackgroundRunnable();
}
private void maybeExecuteBackgroundRunnable() {
if (mBackgroundRunning.compareAndSet(false, true)) {
mExecutor.execute(mBackgroundRunnable);
}
}
private Runnable mBackgroundRunnable = new Runnable() {
@Override
public void run() {
SyncQueueItem msg = mQueue.next();
if (msg == null) {
return;
}
switch (msg.what) {
case REFRESH:
mQueue.removeMessages(REFRESH);
callback.refresh(msg.arg1);
break;
case UPDATE_RANGE:
mQueue.removeMessages(UPDATE_RANGE);
mQueue.removeMessages(LOAD_TILE);
callback.updateRange(
msg.arg1, msg.arg2, msg.arg3, msg.arg4, msg.arg5);
break;
case LOAD_TILE:
callback.loadTile(msg.arg1, msg.arg2);
break;
case RECYCLE_TILE:
//noinspection unchecked
callback.recycleTile((TileList.Tile<T>) msg.data);
break;
default:
Log.e("ThreadUtil", "Unsupported message, what=" + msg.what);
while (true) {
SyncQueueItem msg = mQueue.next();
if (msg == null) {
break;
}
switch (msg.what) {
case REFRESH:
mQueue.removeMessages(REFRESH);
callback.refresh(msg.arg1);
break;
case UPDATE_RANGE:
mQueue.removeMessages(UPDATE_RANGE);
mQueue.removeMessages(LOAD_TILE);
callback.updateRange(
msg.arg1, msg.arg2, msg.arg3, msg.arg4, msg.arg5);
break;
case LOAD_TILE:
callback.loadTile(msg.arg1, msg.arg2);
break;
case RECYCLE_TILE:
//noinspection unchecked
callback.recycleTile((TileList.Tile<T>) msg.data);
break;
default:
Log.e("ThreadUtil", "Unsupported message, what=" + msg.what);
}
}
mBackgroundRunning.set(false);
}
};
};

View File

@ -24,7 +24,7 @@ import java.util.Comparator;
/**
* A Sorted list implementation that can keep items in order and also notify for changes in the
* list
* such that it can be bound to a {@link org.telegram.messenger.support.widget.RecyclerView.Adapter
* such that it can be bound to a {@link android.support.v7.widget.RecyclerView.Adapter
* RecyclerView.Adapter}.
* <p>
* It keeps items ordered using the {@link Callback#compare(Object, Object)} method and uses
@ -737,7 +737,7 @@ public class SortedList<T> {
* so
* that you can change its behavior depending on your UI.
* <p>
* For example, if you are using SortedList with a {@link org.telegram.messenger.support.widget.RecyclerView.Adapter
* For example, if you are using SortedList with a {@link android.support.v7.widget.RecyclerView.Adapter
* RecyclerView.Adapter}, you should
* return whether the items' visual representations are the same or not.
*

View File

@ -16,8 +16,6 @@
package org.telegram.messenger.support.util;
import org.telegram.messenger.support.util.TileList;
interface ThreadUtil<T> {
interface MainThreadCallback<T> {

View File

@ -19,9 +19,6 @@ package org.telegram.messenger.support.widget;
import android.support.v4.util.Pools;
import android.util.Log;
import org.telegram.messenger.support.widget.OpReorderer;
import org.telegram.messenger.support.widget.RecyclerView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -70,6 +67,8 @@ class AdapterHelper implements OpReorderer.Callback {
final OpReorderer mOpReorderer;
private int mExistingUpdateTypes = 0;
AdapterHelper(Callback callback) {
this(callback, false);
}
@ -88,6 +87,7 @@ class AdapterHelper implements OpReorderer.Callback {
void reset() {
recycleUpdateOpsAndClearList(mPendingUpdates);
recycleUpdateOpsAndClearList(mPostponedList);
mExistingUpdateTypes = 0;
}
void preProcess() {
@ -122,6 +122,7 @@ class AdapterHelper implements OpReorderer.Callback {
mCallback.onDispatchSecondPass(mPostponedList.get(i));
}
recycleUpdateOpsAndClearList(mPostponedList);
mExistingUpdateTypes = 0;
}
private void applyMove(UpdateOp op) {
@ -460,6 +461,10 @@ class AdapterHelper implements OpReorderer.Callback {
return mPendingUpdates.size() > 0;
}
boolean hasAnyUpdateTypes(int updateTypes) {
return (mExistingUpdateTypes & updateTypes) != 0;
}
int findPositionOffset(int position) {
return findPositionOffset(position, 0);
}
@ -498,6 +503,7 @@ class AdapterHelper implements OpReorderer.Callback {
*/
boolean onItemRangeChanged(int positionStart, int itemCount, Object payload) {
mPendingUpdates.add(obtainUpdateOp(UpdateOp.UPDATE, positionStart, itemCount, payload));
mExistingUpdateTypes |= UpdateOp.UPDATE;
return mPendingUpdates.size() == 1;
}
@ -506,6 +512,7 @@ class AdapterHelper implements OpReorderer.Callback {
*/
boolean onItemRangeInserted(int positionStart, int itemCount) {
mPendingUpdates.add(obtainUpdateOp(UpdateOp.ADD, positionStart, itemCount, null));
mExistingUpdateTypes |= UpdateOp.ADD;
return mPendingUpdates.size() == 1;
}
@ -514,6 +521,7 @@ class AdapterHelper implements OpReorderer.Callback {
*/
boolean onItemRangeRemoved(int positionStart, int itemCount) {
mPendingUpdates.add(obtainUpdateOp(UpdateOp.REMOVE, positionStart, itemCount, null));
mExistingUpdateTypes |= UpdateOp.REMOVE;
return mPendingUpdates.size() == 1;
}
@ -522,12 +530,13 @@ class AdapterHelper implements OpReorderer.Callback {
*/
boolean onItemRangeMoved(int from, int to, int itemCount) {
if (from == to) {
return false;//no-op
return false; // no-op
}
if (itemCount != 1) {
throw new IllegalArgumentException("Moving more than 1 item is not supported yet");
}
mPendingUpdates.add(obtainUpdateOp(UpdateOp.MOVE, from, to, null));
mExistingUpdateTypes |= UpdateOp.MOVE;
return mPendingUpdates.size() == 1;
}
@ -564,6 +573,7 @@ class AdapterHelper implements OpReorderer.Callback {
}
}
recycleUpdateOpsAndClearList(mPendingUpdates);
mExistingUpdateTypes = 0;
}
public int applyPendingUpdatesToPosition(int position) {
@ -602,18 +612,22 @@ class AdapterHelper implements OpReorderer.Callback {
return position;
}
boolean hasUpdates() {
return !mPostponedList.isEmpty() && !mPendingUpdates.isEmpty();
}
/**
* Queued operation to happen when child views are updated.
*/
static class UpdateOp {
static final int ADD = 0;
static final int ADD = 1;
static final int REMOVE = 1;
static final int REMOVE = 1 << 1;
static final int UPDATE = 2;
static final int UPDATE = 1 << 2;
static final int MOVE = 3;
static final int MOVE = 1 << 3;
static final int POOL_SIZE = 30;

View File

@ -208,8 +208,8 @@ class ChildHelper {
for (int i = 0; i < count; i++) {
final View view = mHiddenViews.get(i);
RecyclerView.ViewHolder holder = mCallback.getChildViewHolder(view);
if (holder.getLayoutPosition() == position && !holder.isInvalid() &&
(type == RecyclerView.INVALID_TYPE || holder.getItemViewType() == type)) {
if (holder.getLayoutPosition() == position && !holder.isInvalid() && !holder.isRemoved()
&& (type == RecyclerView.INVALID_TYPE || holder.getItemViewType() == type)) {
return view;
}
}
@ -339,6 +339,25 @@ class ChildHelper {
}
}
/**
* Moves a child view from hidden list to regular list.
* Calling this method should probably be followed by a detach, otherwise, it will suddenly
* show up in LayoutManager's children list.
*
* @param view The hidden View to unhide
*/
void unhide(View view) {
final int offset = mCallback.indexOfChild(view);
if (offset < 0) {
throw new IllegalArgumentException("view is not a child, cannot hide " + view);
}
if (!mBucket.get(offset)) {
throw new RuntimeException("trying to unhide a view that was not hidden" + view);
}
mBucket.clear(offset);
unhideViewInternal(view);
}
@Override
public String toString() {
return mBucket.toString() + ", hidden list:" + mHiddenViews.size();

View File

@ -15,12 +15,11 @@
*/
package org.telegram.messenger.support.widget;
import android.support.annotation.NonNull;
import android.support.v4.animation.AnimatorCompatHelper;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPropertyAnimatorCompat;
import android.support.v4.view.ViewPropertyAnimatorListener;
import org.telegram.messenger.support.widget.RecyclerView;
import org.telegram.messenger.support.widget.RecyclerView.ViewHolder;
import android.view.View;
@ -34,23 +33,22 @@ import java.util.List;
*
* @see RecyclerView#setItemAnimator(RecyclerView.ItemAnimator)
*/
public class DefaultItemAnimator extends RecyclerView.ItemAnimator {
public class DefaultItemAnimator extends SimpleItemAnimator {
private static final boolean DEBUG = false;
private ArrayList<ViewHolder> mPendingRemovals = new ArrayList<ViewHolder>();
private ArrayList<ViewHolder> mPendingAdditions = new ArrayList<ViewHolder>();
private ArrayList<MoveInfo> mPendingMoves = new ArrayList<MoveInfo>();
private ArrayList<ChangeInfo> mPendingChanges = new ArrayList<ChangeInfo>();
private ArrayList<ViewHolder> mPendingRemovals = new ArrayList<>();
private ArrayList<ViewHolder> mPendingAdditions = new ArrayList<>();
private ArrayList<MoveInfo> mPendingMoves = new ArrayList<>();
private ArrayList<ChangeInfo> mPendingChanges = new ArrayList<>();
private ArrayList<ArrayList<ViewHolder>> mAdditionsList =
new ArrayList<ArrayList<ViewHolder>>();
private ArrayList<ArrayList<MoveInfo>> mMovesList = new ArrayList<ArrayList<MoveInfo>>();
private ArrayList<ArrayList<ChangeInfo>> mChangesList = new ArrayList<ArrayList<ChangeInfo>>();
private ArrayList<ArrayList<ViewHolder>> mAdditionsList = new ArrayList<>();
private ArrayList<ArrayList<MoveInfo>> mMovesList = new ArrayList<>();
private ArrayList<ArrayList<ChangeInfo>> mChangesList = new ArrayList<>();
private ArrayList<ViewHolder> mAddAnimations = new ArrayList<ViewHolder>();
private ArrayList<ViewHolder> mMoveAnimations = new ArrayList<ViewHolder>();
private ArrayList<ViewHolder> mRemoveAnimations = new ArrayList<ViewHolder>();
private ArrayList<ViewHolder> mChangeAnimations = new ArrayList<ViewHolder>();
private ArrayList<ViewHolder> mAddAnimations = new ArrayList<>();
private ArrayList<ViewHolder> mMoveAnimations = new ArrayList<>();
private ArrayList<ViewHolder> mRemoveAnimations = new ArrayList<>();
private ArrayList<ViewHolder> mChangeAnimations = new ArrayList<>();
private static class MoveInfo {
public ViewHolder holder;
@ -112,7 +110,7 @@ public class DefaultItemAnimator extends RecyclerView.ItemAnimator {
mPendingRemovals.clear();
// Next, move stuff
if (movesPending) {
final ArrayList<MoveInfo> moves = new ArrayList<MoveInfo>();
final ArrayList<MoveInfo> moves = new ArrayList<>();
moves.addAll(mPendingMoves);
mMovesList.add(moves);
mPendingMoves.clear();
@ -136,7 +134,7 @@ public class DefaultItemAnimator extends RecyclerView.ItemAnimator {
}
// Next, change stuff, to run in parallel with move animations
if (changesPending) {
final ArrayList<ChangeInfo> changes = new ArrayList<ChangeInfo>();
final ArrayList<ChangeInfo> changes = new ArrayList<>();
changes.addAll(mPendingChanges);
mChangesList.add(changes);
mPendingChanges.clear();
@ -159,7 +157,7 @@ public class DefaultItemAnimator extends RecyclerView.ItemAnimator {
}
// Next, add stuff
if (additionsPending) {
final ArrayList<ViewHolder> additions = new ArrayList<ViewHolder>();
final ArrayList<ViewHolder> additions = new ArrayList<>();
additions.addAll(mPendingAdditions);
mAdditionsList.add(additions);
mPendingAdditions.clear();
@ -312,6 +310,11 @@ public class DefaultItemAnimator extends RecyclerView.ItemAnimator {
@Override
public boolean animateChange(ViewHolder oldHolder, 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.
// run a move animation to handle position changes.
return animateMove(oldHolder, fromX, fromY, toX, toY);
}
final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView);
final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView);
final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView);
@ -322,7 +325,7 @@ public class DefaultItemAnimator extends RecyclerView.ItemAnimator {
ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX);
ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY);
ViewCompat.setAlpha(oldHolder.itemView, prevAlpha);
if (newHolder != null && newHolder.itemView != null) {
if (newHolder != null) {
// carry over translation values
resetAnimation(newHolder);
ViewCompat.setTranslationX(newHolder.itemView, -deltaX);
@ -481,21 +484,25 @@ public class DefaultItemAnimator extends RecyclerView.ItemAnimator {
}
// animations should be ended by the cancel above.
//noinspection PointlessBooleanExpression,ConstantConditions
if (mRemoveAnimations.remove(item) && DEBUG) {
throw new IllegalStateException("after animation is cancelled, item should not be in "
+ "mRemoveAnimations list");
}
//noinspection PointlessBooleanExpression,ConstantConditions
if (mAddAnimations.remove(item) && DEBUG) {
throw new IllegalStateException("after animation is cancelled, item should not be in "
+ "mAddAnimations list");
}
//noinspection PointlessBooleanExpression,ConstantConditions
if (mChangeAnimations.remove(item) && DEBUG) {
throw new IllegalStateException("after animation is cancelled, item should not be in "
+ "mChangeAnimations list");
}
//noinspection PointlessBooleanExpression,ConstantConditions
if (mMoveAnimations.remove(item) && DEBUG) {
throw new IllegalStateException("after animation is cancelled, item should not be in "
+ "mMoveAnimations list");
@ -626,6 +633,28 @@ public class DefaultItemAnimator extends RecyclerView.ItemAnimator {
}
}
/**
* {@inheritDoc}
* <p>
* 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
* 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
* run a move animation instead.
* </li>
* </ul>
*/
@Override
public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
@NonNull List<Object> payloads) {
return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads);
}
private static class VpaListenerAdapter implements ViewPropertyAnimatorListener {
@Override
public void onAnimationStart(View view) {}
@ -635,5 +664,5 @@ public class DefaultItemAnimator extends RecyclerView.ItemAnimator {
@Override
public void onAnimationCancel(View view) {}
};
}
}

View File

@ -37,11 +37,6 @@ public class GridLayoutManager extends LinearLayoutManager {
private static final boolean DEBUG = false;
private static final String TAG = "GridLayoutManager";
public static final int DEFAULT_SPAN_COUNT = -1;
/**
* The measure spec for the scroll direction.
*/
static final int MAIN_DIR_SPEC =
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
/**
* Span size have been changed but we've not done a new layout calculation.
*/
@ -63,6 +58,21 @@ public class GridLayoutManager extends LinearLayoutManager {
// re-used variable to acquire decor insets from RecyclerView
final Rect mDecorInsets = new Rect();
/**
* Constructor used when layout manager is set in XML by RecyclerView attribute
* "layoutManager". If spanCount is not specified in the XML, it defaults to a
* single column.
*
* @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount
*/
public GridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
Properties properties = getProperties(context, attrs, defStyleAttr, defStyleRes);
setSpanCount(properties.spanCount);
}
/**
* Creates a vertical GridLayoutManager
*
@ -110,7 +120,9 @@ public class GridLayoutManager extends LinearLayoutManager {
if (state.getItemCount() < 1) {
return 0;
}
return getSpanGroupIndex(recycler, state, state.getItemCount() - 1);
// Row count is one more than the last item's row index.
return getSpanGroupIndex(recycler, state, state.getItemCount() - 1) + 1;
}
@Override
@ -122,7 +134,9 @@ public class GridLayoutManager extends LinearLayoutManager {
if (state.getItemCount() < 1) {
return 0;
}
return getSpanGroupIndex(recycler, state, state.getItemCount() - 1);
// Column count is one more than the last item's column index.
return getSpanGroupIndex(recycler, state, state.getItemCount() - 1) + 1;
}
@Override
@ -206,8 +220,13 @@ public class GridLayoutManager extends LinearLayoutManager {
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
if (mOrientation == HORIZONTAL) {
return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.FILL_PARENT);
} else {
return new LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
}
@Override
@ -258,47 +277,124 @@ public class GridLayoutManager extends LinearLayoutManager {
calculateItemBorders(totalSpace);
}
private void calculateItemBorders(int totalSpace) {
if (mCachedBorders == null || mCachedBorders.length != mSpanCount + 1
|| mCachedBorders[mCachedBorders.length - 1] != totalSpace) {
mCachedBorders = new int[mSpanCount + 1];
@Override
public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
if (mCachedBorders == null) {
super.setMeasuredDimension(childrenBounds, wSpec, hSpec);
}
mCachedBorders[0] = 0;
int sizePerSpan = totalSpace / mSpanCount;
int sizePerSpanRemainder = totalSpace % mSpanCount;
final int width, height;
final int horizontalPadding = getPaddingLeft() + getPaddingRight();
final int verticalPadding = getPaddingTop() + getPaddingBottom();
if (mOrientation == VERTICAL) {
final int usedHeight = childrenBounds.height() + verticalPadding;
height = chooseSize(hSpec, usedHeight, getMinimumHeight());
width = chooseSize(wSpec, mCachedBorders[mCachedBorders.length - 1] + horizontalPadding,
getMinimumWidth());
} else {
final int usedWidth = childrenBounds.width() + horizontalPadding;
width = chooseSize(wSpec, usedWidth, getMinimumWidth());
height = chooseSize(hSpec, mCachedBorders[mCachedBorders.length - 1] + verticalPadding,
getMinimumHeight());
}
setMeasuredDimension(width, height);
}
/**
* @param totalSpace Total available space after padding is removed
*/
private void calculateItemBorders(int totalSpace) {
mCachedBorders = calculateItemBorders(mCachedBorders, mSpanCount, totalSpace);
}
/**
* @param cachedBorders The out array
* @param spanCount number of spans
* @param totalSpace total available space after padding is removed
* @return The updated array. Might be the same instance as the provided array if its size
* has not changed.
*/
static int[] calculateItemBorders(int[] cachedBorders, int spanCount, int totalSpace) {
if (cachedBorders == null || cachedBorders.length != spanCount + 1
|| cachedBorders[cachedBorders.length - 1] != totalSpace) {
cachedBorders = new int[spanCount + 1];
}
cachedBorders[0] = 0;
int sizePerSpan = totalSpace / spanCount;
int sizePerSpanRemainder = totalSpace % spanCount;
int consumedPixels = 0;
int additionalSize = 0;
for (int i = 1; i <= mSpanCount; i++) {
for (int i = 1; i <= spanCount; i++) {
int itemSize = sizePerSpan;
additionalSize += sizePerSpanRemainder;
if (additionalSize > 0 && (mSpanCount - additionalSize) < sizePerSpanRemainder) {
if (additionalSize > 0 && (spanCount - additionalSize) < sizePerSpanRemainder) {
itemSize += 1;
additionalSize -= mSpanCount;
additionalSize -= spanCount;
}
consumedPixels += itemSize;
mCachedBorders[i] = consumedPixels;
cachedBorders[i] = consumedPixels;
}
return cachedBorders;
}
@Override
void onAnchorReady(RecyclerView.Recycler recycler, RecyclerView.State state,
AnchorInfo anchorInfo) {
super.onAnchorReady(recycler, state, anchorInfo);
AnchorInfo anchorInfo, int itemDirection) {
super.onAnchorReady(recycler, state, anchorInfo, itemDirection);
updateMeasurements();
if (state.getItemCount() > 0 && !state.isPreLayout()) {
ensureAnchorIsInFirstSpan(recycler, state, anchorInfo);
ensureAnchorIsInCorrectSpan(recycler, state, anchorInfo, itemDirection);
}
ensureViewSet();
}
private void ensureViewSet() {
if (mSet == null || mSet.length != mSpanCount) {
mSet = new View[mSpanCount];
}
}
private void ensureAnchorIsInFirstSpan(RecyclerView.Recycler recycler, RecyclerView.State state,
AnchorInfo anchorInfo) {
@Override
public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
RecyclerView.State state) {
updateMeasurements();
ensureViewSet();
return super.scrollHorizontallyBy(dx, recycler, state);
}
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
RecyclerView.State state) {
updateMeasurements();
ensureViewSet();
return super.scrollVerticallyBy(dy, recycler, state);
}
private void ensureAnchorIsInCorrectSpan(RecyclerView.Recycler recycler,
RecyclerView.State state, AnchorInfo anchorInfo, int itemDirection) {
final boolean layingOutInPrimaryDirection =
itemDirection == LayoutState.ITEM_DIRECTION_TAIL;
int span = getSpanIndex(recycler, state, anchorInfo.mPosition);
while (span > 0 && anchorInfo.mPosition > 0) {
anchorInfo.mPosition--;
span = getSpanIndex(recycler, state, anchorInfo.mPosition);
if (layingOutInPrimaryDirection) {
// choose span 0
while (span > 0 && anchorInfo.mPosition > 0) {
anchorInfo.mPosition--;
span = getSpanIndex(recycler, state, anchorInfo.mPosition);
}
} else {
// choose the max span we can get. hopefully last one
final int indexLimit = state.getItemCount() - 1;
int pos = anchorInfo.mPosition;
int bestSpan = span;
while (pos < indexLimit) {
int next = getSpanIndex(recycler, state, pos + 1);
if (next > bestSpan) {
pos += 1;
bestSpan = next;
} else {
break;
}
}
anchorInfo.mPosition = pos;
}
}
@ -398,6 +494,15 @@ public class GridLayoutManager extends LinearLayoutManager {
@Override
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
LayoutState layoutState, LayoutChunkResult result) {
final int otherDirSpecMode = mOrientationHelper.getModeInOther();
final boolean flexibleInOtherDir = otherDirSpecMode != View.MeasureSpec.EXACTLY;
final int currentOtherDirSize = getChildCount() > 0 ? mCachedBorders[mSpanCount] : 0;
// if grid layout's dimensions are not specified, let the new row change the measurements
// This is not perfect since we not covering all rows but still solves an important case
// where they may have a header row which should be laid out according to children.
if (flexibleInOtherDir) {
updateMeasurements(); // reset measurements
}
final boolean layingOutInPrimaryDirection =
layoutState.mItemDirection == LayoutState.ITEM_DIRECTION_TAIL;
int count = 0;
@ -435,6 +540,7 @@ public class GridLayoutManager extends LinearLayoutManager {
}
int maxSize = 0;
float maxSizeInOther = 0; // use a float to get size per span
// we should assign spans before item decor offsets are calculated
assignSpans(recycler, state, count, consumedSpanCount, layingOutInPrimaryDirection);
@ -455,35 +561,73 @@ public class GridLayoutManager extends LinearLayoutManager {
}
final LayoutParams lp = (LayoutParams) view.getLayoutParams();
final int spec = View.MeasureSpec.makeMeasureSpec(
mCachedBorders[lp.mSpanIndex + lp.mSpanSize] -
mCachedBorders[lp.mSpanIndex],
View.MeasureSpec.EXACTLY);
final int spec = getChildMeasureSpec(mCachedBorders[lp.mSpanIndex + lp.mSpanSize] -
mCachedBorders[lp.mSpanIndex], otherDirSpecMode, 0,
mOrientation == HORIZONTAL ? lp.height : lp.width,
false);
final int mainSpec = getChildMeasureSpec(mOrientationHelper.getTotalSpace(),
mOrientationHelper.getMode(), 0,
mOrientation == VERTICAL ? lp.height : lp.width, true);
// Unless the child has MATCH_PARENT, measure it from its specs before adding insets.
if (mOrientation == VERTICAL) {
measureChildWithDecorationsAndMargin(view, spec, getMainDirSpec(lp.height), false);
@SuppressWarnings("deprecation")
final boolean applyInsets = lp.height == ViewGroup.LayoutParams.FILL_PARENT;
measureChildWithDecorationsAndMargin(view, spec, mainSpec, applyInsets, false);
} else {
measureChildWithDecorationsAndMargin(view, getMainDirSpec(lp.width), spec, false);
//noinspection deprecation
final boolean applyInsets = lp.width == ViewGroup.LayoutParams.FILL_PARENT;
measureChildWithDecorationsAndMargin(view, mainSpec, spec, applyInsets, false);
}
final int size = mOrientationHelper.getDecoratedMeasurement(view);
if (size > maxSize) {
maxSize = size;
}
final float otherSize = 1f * mOrientationHelper.getDecoratedMeasurementInOther(view) /
lp.mSpanSize;
if (otherSize > maxSizeInOther) {
maxSizeInOther = otherSize;
}
}
// views that did not measure the maxSize has to be re-measured
final int maxMeasureSpec = getMainDirSpec(maxSize);
if (flexibleInOtherDir) {
// re-distribute columns
guessMeasurement(maxSizeInOther, currentOtherDirSize);
// now we should re-measure any item that was match parent.
maxSize = 0;
for (int i = 0; i < count; i++) {
View view = mSet[i];
final LayoutParams lp = (LayoutParams) view.getLayoutParams();
final int spec = getChildMeasureSpec(mCachedBorders[lp.mSpanIndex + lp.mSpanSize] -
mCachedBorders[lp.mSpanIndex], View.MeasureSpec.EXACTLY, 0,
mOrientation == HORIZONTAL ? lp.height : lp.width, false);
final int mainSpec = getChildMeasureSpec(mOrientationHelper.getTotalSpace(),
mOrientationHelper.getMode(), 0,
mOrientation == VERTICAL ? lp.height : lp.width, true);
if (mOrientation == VERTICAL) {
measureChildWithDecorationsAndMargin(view, spec, mainSpec, false, true);
} else {
measureChildWithDecorationsAndMargin(view, mainSpec, spec, false, true);
}
final int size = mOrientationHelper.getDecoratedMeasurement(view);
if (size > maxSize) {
maxSize = size;
}
}
}
// Views that did not measure the maxSize has to be re-measured
// We will stop doing this once we introduce Gravity in the GLM layout params
final int maxMeasureSpec = View.MeasureSpec.makeMeasureSpec(maxSize,
View.MeasureSpec.EXACTLY);
for (int i = 0; i < count; i ++) {
final View view = mSet[i];
if (mOrientationHelper.getDecoratedMeasurement(view) != maxSize) {
final LayoutParams lp = (LayoutParams) view.getLayoutParams();
final int spec = View.MeasureSpec.makeMeasureSpec(
mCachedBorders[lp.mSpanIndex + lp.mSpanSize] -
mCachedBorders[lp.mSpanIndex],
View.MeasureSpec.EXACTLY);
final int spec = getChildMeasureSpec(mCachedBorders[lp.mSpanIndex + lp.mSpanSize]
- mCachedBorders[lp.mSpanIndex], View.MeasureSpec.EXACTLY, 0,
mOrientation == HORIZONTAL ? lp.height : lp.width, false);
if (mOrientation == VERTICAL) {
measureChildWithDecorationsAndMargin(view, spec, maxMeasureSpec, true);
measureChildWithDecorationsAndMargin(view, spec, maxMeasureSpec, true, true);
} else {
measureChildWithDecorationsAndMargin(view, maxMeasureSpec, spec, true);
measureChildWithDecorationsAndMargin(view, maxMeasureSpec, spec, true, true);
}
}
}
@ -512,8 +656,13 @@ public class GridLayoutManager extends LinearLayoutManager {
View view = mSet[i];
LayoutParams params = (LayoutParams) view.getLayoutParams();
if (mOrientation == VERTICAL) {
left = getPaddingLeft() + mCachedBorders[params.mSpanIndex];
right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
if (isLayoutRTL()) {
right = getPaddingLeft() + mCachedBorders[params.mSpanIndex + params.mSpanSize];
left = right - mOrientationHelper.getDecoratedMeasurementInOther(view);
} else {
left = getPaddingLeft() + mCachedBorders[params.mSpanIndex];
right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
}
} else {
top = getPaddingTop() + mCachedBorders[params.mSpanIndex];
bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view);
@ -537,16 +686,24 @@ public class GridLayoutManager extends LinearLayoutManager {
Arrays.fill(mSet, null);
}
private int getMainDirSpec(int dim) {
if (dim < 0) {
return MAIN_DIR_SPEC;
} else {
return View.MeasureSpec.makeMeasureSpec(dim, View.MeasureSpec.EXACTLY);
}
/**
* This is called after laying out a row (if vertical) or a column (if horizontal) when the
* RecyclerView does not have exact measurement specs.
* <p>
* Here we try to assign a best guess width or height and re-do the layout to update other
* views that wanted to FILL_PARENT in the non-scroll orientation.
*
* @param maxSizeInOther The maximum size per span ratio from the measurement of the children.
* @param currentOtherDirSize The size before this layout chunk. There is no reason to go below.
*/
private void guessMeasurement(float maxSizeInOther, int currentOtherDirSize) {
final int contentSize = Math.round(maxSizeInOther * mSpanCount);
// always re-calculate because borders were stretched during the fill
calculateItemBorders(Math.max(contentSize, currentOtherDirSize));
}
private void measureChildWithDecorationsAndMargin(View child, int widthSpec, int heightSpec,
boolean capBothSpecs) {
boolean capBothSpecs, boolean alreadyMeasured) {
calculateItemDecorationsForChild(child, mDecorInsets);
RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
if (capBothSpecs || mOrientation == VERTICAL) {
@ -557,7 +714,16 @@ public class GridLayoutManager extends LinearLayoutManager {
heightSpec = updateSpecWithExtra(heightSpec, lp.topMargin + mDecorInsets.top,
lp.bottomMargin + mDecorInsets.bottom);
}
child.measure(widthSpec, heightSpec);
final boolean measure;
if (alreadyMeasured) {
measure = shouldReMeasureChild(child, widthSpec, heightSpec, lp);
} else {
measure = shouldMeasureChild(child, widthSpec, heightSpec, lp);
}
if (measure) {
child.measure(widthSpec, heightSpec);
}
}
private int updateSpecWithExtra(int spec, int startInset, int endInset) {
@ -567,7 +733,7 @@ public class GridLayoutManager extends LinearLayoutManager {
final int mode = View.MeasureSpec.getMode(spec);
if (mode == View.MeasureSpec.AT_MOST || mode == View.MeasureSpec.EXACTLY) {
return View.MeasureSpec.makeMeasureSpec(
View.MeasureSpec.getSize(spec) - startInset - endInset, mode);
Math.max(0, View.MeasureSpec.getSize(spec) - startInset - endInset), mode);
}
return spec;
}
@ -806,6 +972,78 @@ public class GridLayoutManager extends LinearLayoutManager {
}
}
@Override
public View onFocusSearchFailed(View focused, int focusDirection,
RecyclerView.Recycler recycler, RecyclerView.State state) {
View prevFocusedChild = findContainingItemView(focused);
if (prevFocusedChild == null) {
return null;
}
LayoutParams lp = (LayoutParams) prevFocusedChild.getLayoutParams();
final int prevSpanStart = lp.mSpanIndex;
final int prevSpanEnd = lp.mSpanIndex + lp.mSpanSize;
View view = super.onFocusSearchFailed(focused, focusDirection, recycler, state);
if (view == null) {
return null;
}
// LinearLayoutManager finds the last child. What we want is the child which has the same
// spanIndex.
final int layoutDir = convertFocusDirectionToLayoutDirection(focusDirection);
final boolean ascend = (layoutDir == LayoutState.LAYOUT_END) != mShouldReverseLayout;
final int start, inc, limit;
if (ascend) {
start = getChildCount() - 1;
inc = -1;
limit = -1;
} else {
start = 0;
inc = 1;
limit = getChildCount();
}
final boolean preferLastSpan = mOrientation == VERTICAL && isLayoutRTL();
View weakCandidate = null; // somewhat matches but not strong
int weakCandidateSpanIndex = -1;
int weakCandidateOverlap = 0; // how many spans overlap
for (int i = start; i != limit; i += inc) {
View candidate = getChildAt(i);
if (candidate == prevFocusedChild) {
break;
}
if (!candidate.isFocusable()) {
continue;
}
final LayoutParams candidateLp = (LayoutParams) candidate.getLayoutParams();
final int candidateStart = candidateLp.mSpanIndex;
final int candidateEnd = candidateLp.mSpanIndex + candidateLp.mSpanSize;
if (candidateStart == prevSpanStart && candidateEnd == prevSpanEnd) {
return candidate; // perfect match
}
boolean assignAsWeek = false;
if (weakCandidate == null) {
assignAsWeek = true;
} else {
int maxStart = Math.max(candidateStart, prevSpanStart);
int minEnd = Math.min(candidateEnd, prevSpanEnd);
int overlap = minEnd - maxStart;
if (overlap > weakCandidateOverlap) {
assignAsWeek = true;
} else if (overlap == weakCandidateOverlap &&
preferLastSpan == (candidateStart > weakCandidateSpanIndex)) {
assignAsWeek = true;
}
}
if (assignAsWeek) {
weakCandidate = candidate;
weakCandidateSpanIndex = candidateLp.mSpanIndex;
weakCandidateOverlap = Math.min(candidateEnd, prevSpanEnd) -
Math.max(candidateStart, prevSpanStart);
}
}
return weakCandidate;
}
@Override
public boolean supportsPredictiveItemAnimations() {
return mPendingSavedState == null && !mPendingSpanCountChange;

View File

@ -15,6 +15,7 @@
*/
package org.telegram.messenger.support.widget;
import android.view.View;
/**
@ -35,8 +36,11 @@ class LayoutState {
final static int ITEM_DIRECTION_TAIL = 1;
final static int SCOLLING_OFFSET_NaN = Integer.MIN_VALUE;
/**
* We may not want to recycle children in some cases (e.g. layout)
*/
boolean mRecycle = true;
/**
* Number of pixels that we should fill, in the layout direction.
*/
@ -69,6 +73,16 @@ class LayoutState {
*/
int mEndLine = 0;
/**
* If true, layout should stop if a focusable view is added
*/
boolean mStopInFocusable;
/**
* If the content is not wrapped with any value
*/
boolean mInfinite;
/**
* @return true if there are more items in the data adapter
*/

View File

@ -16,6 +16,8 @@
package org.telegram.messenger.support.widget;
import static org.telegram.messenger.support.widget.RecyclerView.NO_POSITION;
import android.content.Context;
import android.graphics.PointF;
import android.os.Parcel;
@ -23,20 +25,18 @@ import android.os.Parcelable;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.accessibility.AccessibilityEventCompat;
import android.support.v4.view.accessibility.AccessibilityRecordCompat;
import org.telegram.messenger.support.widget.RecyclerView.LayoutParams;
import org.telegram.messenger.support.widget.helper.ItemTouchHelper;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import org.telegram.messenger.support.widget.RecyclerView.LayoutParams;
import java.util.List;
import static org.telegram.messenger.support.widget.RecyclerView.NO_POSITION;
/**
* A {@link RecyclerView.LayoutManager} implementation which provides
* A {@link android.support.v7.widget.RecyclerView.LayoutManager} implementation which provides
* similar functionality to {@link android.widget.ListView}.
*/
public class LinearLayoutManager extends RecyclerView.LayoutManager implements
@ -58,7 +58,7 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
* than this factor times the total space of the list. If layout is vertical, total space is the
* height minus padding, if layout is horizontal, total space is the width minus padding.
*/
private static final float MAX_SCROLL_FACTOR = 0.33f;
private static final float MAX_SCROLL_FACTOR = 1 / 3f;
/**
@ -154,6 +154,24 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
public LinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
setOrientation(orientation);
setReverseLayout(reverseLayout);
setAutoMeasureEnabled(true);
}
/**
* Constructor used when layout manager is set in XML by RecyclerView attribute
* "layoutManager". Defaults to vertical orientation.
*
* @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation
* @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout
* @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd
*/
public LinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
Properties properties = getProperties(context, attrs, defStyleAttr, defStyleRes);
setOrientation(properties.orientation);
setReverseLayout(properties.reverseLayout);
setStackFromEnd(properties.stackFromEnd);
setAutoMeasureEnabled(true);
}
/**
@ -288,8 +306,7 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
/**
* Returns the current orientaion of the layout.
*
* @return Current orientation.
* @see #mOrientation
* @return Current orientation, either {@link #HORIZONTAL} or {@link #VERTICAL}
* @see #setOrientation(int)
*/
public int getOrientation() {
@ -297,7 +314,7 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
}
/**
* Sets the orientation of the layout. {@link org.telegram.messenger.support.widget.LinearLayoutManager}
* Sets the orientation of the layout. {@link android.support.v7.widget.LinearLayoutManager}
* will do its best to keep scroll position.
*
* @param orientation {@link #HORIZONTAL} or {@link #VERTICAL}
@ -333,7 +350,7 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
* Returns if views are laid out from the opposite direction of the layout.
*
* @return If layout is reversed or not.
* @see {@link #setReverseLayout(boolean)}
* @see #setReverseLayout(boolean)
*/
public boolean getReverseLayout() {
return mReverseLayout;
@ -345,8 +362,8 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
* laid out at the end of the UI, second item is laid out before it etc.
*
* For horizontal layouts, it depends on the layout direction.
* When set to true, If {@link RecyclerView} is LTR, than it will
* layout from RTL, if {@link RecyclerView}} is RTL, it will layout
* When set to true, If {@link android.support.v7.widget.RecyclerView} is LTR, than it will
* layout from RTL, if {@link android.support.v7.widget.RecyclerView}} is RTL, it will layout
* from LTR.
*
* If you are looking for the exact same behavior of
@ -385,7 +402,7 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
/**
* <p>Returns the amount of extra space that should be laid out by LayoutManager.
* By default, {@link org.telegram.messenger.support.widget.LinearLayoutManager} lays out 1 extra page of
* By default, {@link android.support.v7.widget.LinearLayoutManager} lays out 1 extra page of
* items while smooth scrolling and 0 otherwise. You can override this method to implement your
* custom layout pre-cache logic.</p>
* <p>Laying out invisible elements will eventually come with performance cost. On the other
@ -512,8 +529,18 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
}
int startOffset;
int endOffset;
onAnchorReady(recycler, state, mAnchorInfo);
final int firstLayoutDirection;
if (mAnchorInfo.mLayoutFromEnd) {
firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL :
LayoutState.ITEM_DIRECTION_HEAD;
} else {
firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD :
LayoutState.ITEM_DIRECTION_TAIL;
}
onAnchorReady(recycler, state, mAnchorInfo, firstLayoutDirection);
detachAndScrapAttachedViews(recycler);
mLayoutState.mInfinite = mOrientationHelper.getMode() == View.MeasureSpec.UNSPECIFIED;
mLayoutState.mIsPreLayout = state.isPreLayout();
if (mAnchorInfo.mLayoutFromEnd) {
// fill towards start
@ -606,13 +633,14 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
/**
* Method called when Anchor position is decided. Extending class can setup accordingly or
* even update anchor info if necessary.
*
* @param recycler
* @param state
* @param anchorInfo Simple data structure to keep anchor point information for the next layout
* @param recycler The recycler for the layout
* @param state The layout state
* @param anchorInfo The mutable POJO that keeps the position and offset.
* @param firstLayoutItemDirection The direction of the first layout filling in terms of adapter
* indices.
*/
void onAnchorReady(RecyclerView.Recycler recycler, RecyclerView.State state,
AnchorInfo anchorInfo) {
AnchorInfo anchorInfo, int firstLayoutItemDirection) {
}
/**
@ -1100,9 +1128,10 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
private void updateLayoutState(int layoutDirection, int requiredSpace,
boolean canUseExistingSpace, RecyclerView.State state) {
mLayoutState.mInfinite = mOrientationHelper.getMode() == View.MeasureSpec.UNSPECIFIED;
mLayoutState.mExtra = getExtraLayoutSpace(state);
mLayoutState.mLayoutDirection = layoutDirection;
int fastScrollSpace;
int scrollingOffset;
if (layoutDirection == LayoutState.LAYOUT_END) {
mLayoutState.mExtra += mOrientationHelper.getEndPadding();
// get the first child in the direction we are going
@ -1113,7 +1142,7 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection;
mLayoutState.mOffset = mOrientationHelper.getDecoratedEnd(child);
// calculate how much we can scroll without adding new children (independent of layout)
fastScrollSpace = mOrientationHelper.getDecoratedEnd(child)
scrollingOffset = mOrientationHelper.getDecoratedEnd(child)
- mOrientationHelper.getEndAfterPadding();
} else {
@ -1123,14 +1152,14 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
: LayoutState.ITEM_DIRECTION_HEAD;
mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection;
mLayoutState.mOffset = mOrientationHelper.getDecoratedStart(child);
fastScrollSpace = -mOrientationHelper.getDecoratedStart(child)
scrollingOffset = -mOrientationHelper.getDecoratedStart(child)
+ mOrientationHelper.getStartAfterPadding();
}
mLayoutState.mAvailable = requiredSpace;
if (canUseExistingSpace) {
mLayoutState.mAvailable -= fastScrollSpace;
mLayoutState.mAvailable -= scrollingOffset;
}
mLayoutState.mScrollingOffset = fastScrollSpace;
mLayoutState.mScrollingOffset = scrollingOffset;
}
int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
@ -1142,8 +1171,8 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
final int layoutDirection = dy > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;
final int absDy = Math.abs(dy);
updateLayoutState(layoutDirection, absDy, true, state);
final int freeScroll = mLayoutState.mScrollingOffset;
final int consumed = freeScroll + fill(recycler, mLayoutState, state, false);
final int consumed = mLayoutState.mScrollingOffset
+ fill(recycler, mLayoutState, state, false);
if (consumed < 0) {
if (DEBUG) {
Log.d(TAG, "Don't have any more elements to scroll");
@ -1193,7 +1222,7 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
/**
* Recycles views that went out of bounds after scrolling towards the end of the layout.
*
* @param recycler Recycler instance of {@link RecyclerView}
* @param recycler Recycler instance of {@link android.support.v7.widget.RecyclerView}
* @param dt This can be used to add additional padding to the visible area. This is used
* to detect children that will go out of bounds after scrolling, without
* actually moving them.
@ -1232,7 +1261,7 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
/**
* Recycles views that went out of bounds after scrolling towards the start of the layout.
*
* @param recycler Recycler instance of {@link RecyclerView}
* @param recycler Recycler instance of {@link android.support.v7.widget.RecyclerView}
* @param dt This can be used to add additional padding to the visible area. This is used
* to detect children that will go out of bounds after scrolling, without
* actually moving them.
@ -1274,12 +1303,12 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
* @param layoutState Current layout state. Right now, this object does not change but
* we may consider moving it out of this view so passing around as a
* parameter for now, rather than accessing {@link #mLayoutState}
* @see #recycleViewsFromStart(RecyclerView.Recycler, int)
* @see #recycleViewsFromEnd(RecyclerView.Recycler, int)
* @see org.telegram.messenger.support.widget.LinearLayoutManager.LayoutState#mLayoutDirection
* @see #recycleViewsFromStart(android.support.v7.widget.RecyclerView.Recycler, int)
* @see #recycleViewsFromEnd(android.support.v7.widget.RecyclerView.Recycler, int)
* @see android.support.v7.widget.LinearLayoutManager.LayoutState#mLayoutDirection
*/
private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) {
if (!layoutState.mRecycle) {
if (!layoutState.mRecycle || layoutState.mInfinite) {
return;
}
if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
@ -1291,7 +1320,7 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
/**
* The magic functions :). Fills the given layout, defined by the layoutState. This is fairly
* independent from the rest of the {@link org.telegram.messenger.support.widget.LinearLayoutManager}
* independent from the rest of the {@link android.support.v7.widget.LinearLayoutManager}
* and with little change, can be made publicly available as a helper class.
*
* @param recycler Current recycler that is attached to RecyclerView
@ -1313,7 +1342,7 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
}
int remainingSpace = layoutState.mAvailable + layoutState.mExtra;
LayoutChunkResult layoutChunkResult = new LayoutChunkResult();
while (remainingSpace > 0 && layoutState.hasMore(state)) {
while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
layoutChunkResult.resetInternal();
layoutChunk(recycler, state, layoutState, layoutChunkResult);
if (layoutChunkResult.mFinished) {
@ -1424,6 +1453,13 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
result.mFocusable = view.isFocusable();
}
@Override
boolean shouldMeasureTwice() {
return getHeightMode() != View.MeasureSpec.EXACTLY
&& getWidthMode() != View.MeasureSpec.EXACTLY
&& hasFlexibleChildInBothOrientations();
}
/**
* Converts a focusDirection to orientation.
*
@ -1434,7 +1470,7 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
* @return {@link LayoutState#LAYOUT_START} or {@link LayoutState#LAYOUT_END} if focus direction
* is applicable to current state, {@link LayoutState#INVALID_LAYOUT} otherwise.
*/
private int convertFocusDirectionToLayoutDirection(int focusDirection) {
int convertFocusDirectionToLayoutDirection(int focusDirection) {
switch (focusDirection) {
case View.FOCUS_BACKWARD:
return LayoutState.LAYOUT_START;
@ -1916,7 +1952,8 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
boolean mIsPreLayout = false;
/**
* The most recent {@link #scrollBy(int, RecyclerView.Recycler, RecyclerView.State)} amount.
* The most recent {@link #scrollBy(int, RecyclerView.Recycler, RecyclerView.State)}
* amount.
*/
int mLastScrollDelta;
@ -1926,6 +1963,11 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
*/
List<RecyclerView.ViewHolder> mScrapList = null;
/**
* Used when there is no limit in how many views can be laid out.
*/
boolean mInfinite;
/**
* @return true if there are more items in the data adapter
*/
@ -2020,7 +2062,10 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
}
}
static class SavedState implements Parcelable {
/**
* @hide
*/
public static class SavedState implements Parcelable {
int mAnchorPosition;

View File

@ -24,8 +24,6 @@ import android.view.View;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.LinearInterpolator;
import org.telegram.messenger.support.widget.RecyclerView;
/**
* {@link RecyclerView.SmoothScroller} implementation which uses
* {@link android.view.animation.LinearInterpolator} until the target position becames a child of
@ -124,6 +122,7 @@ abstract public class LinearSmoothScroller extends RecyclerView.SmoothScroller {
stop();
return;
}
//noinspection PointlessBooleanExpression
if (DEBUG && mTargetVector != null
&& ((mTargetVector.x * dx < 0 || mTargetVector.y * dy < 0))) {
throw new IllegalStateException("Scroll happened in the opposite direction"
@ -293,13 +292,13 @@ abstract public class LinearSmoothScroller extends RecyclerView.SmoothScroller {
* @param view The view which we want to make fully visible
* @param snapPreference The edge which the view should snap to when entering the visible
* area. One of {@link #SNAP_TO_START}, {@link #SNAP_TO_END} or
* {@link #SNAP_TO_END}.
* {@link #SNAP_TO_ANY}.
* @return The vertical scroll amount necessary to make the view visible with the given
* snap preference.
*/
public int calculateDyToMakeVisible(View view, int snapPreference) {
final RecyclerView.LayoutManager layoutManager = getLayoutManager();
if (!layoutManager.canScrollVertically()) {
if (layoutManager == null || !layoutManager.canScrollVertically()) {
return 0;
}
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
@ -324,7 +323,7 @@ abstract public class LinearSmoothScroller extends RecyclerView.SmoothScroller {
*/
public int calculateDxToMakeVisible(View view, int snapPreference) {
final RecyclerView.LayoutManager layoutManager = getLayoutManager();
if (!layoutManager.canScrollHorizontally()) {
if (layoutManager == null || !layoutManager.canScrollHorizontally()) {
return 0;
}
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)

View File

@ -19,8 +19,6 @@ package org.telegram.messenger.support.widget;
import android.view.View;
import android.widget.LinearLayout;
import org.telegram.messenger.support.widget.RecyclerView;
/**
* Helper class for LayoutManagers to abstract measurements depending on the View's orientation.
* <p>
@ -167,6 +165,28 @@ public abstract class OrientationHelper {
*/
public abstract int getEndPadding();
/**
* Returns the MeasureSpec mode for the current orientation from the LayoutManager.
*
* @return The current measure spec mode.
*
* @see View.MeasureSpec
* @see RecyclerView.LayoutManager#getWidthMode()
* @see RecyclerView.LayoutManager#getHeightMode()
*/
public abstract int getMode();
/**
* Returns the MeasureSpec mode for the perpendicular orientation from the LayoutManager.
*
* @return The current measure spec mode.
*
* @see View.MeasureSpec
* @see RecyclerView.LayoutManager#getWidthMode()
* @see RecyclerView.LayoutManager#getHeightMode()
*/
public abstract int getModeInOther();
/**
* Creates an OrientationHelper for the given LayoutManager and orientation.
*
@ -259,6 +279,16 @@ public abstract class OrientationHelper {
public int getEndPadding() {
return mLayoutManager.getPaddingRight();
}
@Override
public int getMode() {
return mLayoutManager.getWidthMode();
}
@Override
public int getModeInOther() {
return mLayoutManager.getHeightMode();
}
};
}
@ -335,6 +365,16 @@ public abstract class OrientationHelper {
public int getEndPadding() {
return mLayoutManager.getPaddingBottom();
}
@Override
public int getMode() {
return mLayoutManager.getHeightMode();
}
@Override
public int getModeInOther() {
return mLayoutManager.getWidthMode();
}
};
}
}

View File

@ -22,8 +22,6 @@ import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import org.telegram.messenger.support.widget.RecyclerView;
/**
* The AccessibilityDelegate used by RecyclerView.
* <p>

View File

@ -17,8 +17,6 @@ package org.telegram.messenger.support.widget;
import android.view.View;
import org.telegram.messenger.support.widget.RecyclerView;
/**
* A helper class to do scroll offset calculations.
*/

View File

@ -0,0 +1,442 @@
package org.telegram.messenger.support.widget;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.telegram.messenger.support.widget.RecyclerView.Adapter;
import org.telegram.messenger.support.widget.RecyclerView.ViewHolder;
import org.telegram.messenger.support.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
import android.util.Log;
import android.view.View;
import java.util.List;
/**
* A wrapper class for ItemAnimator that records View bounds and decides whether it should run
* move, change, add or remove animations. This class also replicates the original ItemAnimator
* API.
* <p>
* It uses {@link ItemHolderInfo} to track the bounds information of the Views. If you would like
* to
* extend this class, you can override {@link #obtainHolderInfo()} method to provide your own info
* class that extends {@link ItemHolderInfo}.
*/
abstract public class SimpleItemAnimator extends RecyclerView.ItemAnimator {
private static final boolean DEBUG = false;
private static final String TAG = "SimpleItemAnimator";
boolean mSupportsChangeAnimations = true;
/**
* Returns whether this ItemAnimator supports animations of change events.
*
* @return true if change animations are supported, false otherwise
*/
@SuppressWarnings("unused")
public boolean getSupportsChangeAnimations() {
return mSupportsChangeAnimations;
}
/**
* Sets whether this ItemAnimator supports animations of item change events.
* If you set this property to false, actions on the data set which change the
* contents of items will not be animated. What those animations do is left
* up to the discretion of the ItemAnimator subclass, in its
* {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} implementation.
* The value of this property is true by default.
*
* @param supportsChangeAnimations true if change animations are supported by
* this ItemAnimator, false otherwise. If the property is false,
* the ItemAnimator
* will not receive a call to
* {@link #animateChange(ViewHolder, ViewHolder, int, int, int,
* int)} when changes occur.
* @see Adapter#notifyItemChanged(int)
* @see Adapter#notifyItemRangeChanged(int, int)
*/
public void setSupportsChangeAnimations(boolean supportsChangeAnimations) {
mSupportsChangeAnimations = supportsChangeAnimations;
}
/**
* {@inheritDoc}
*
* @return True if change animations are not supported or the ViewHolder is invalid,
* false otherwise.
*
* @see #setSupportsChangeAnimations(boolean)
*/
@Override
public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder) {
return !mSupportsChangeAnimations || viewHolder.isInvalid();
}
@Override
public boolean animateDisappearance(@NonNull ViewHolder viewHolder,
@NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
int oldLeft = preLayoutInfo.left;
int oldTop = preLayoutInfo.top;
View disappearingItemView = viewHolder.itemView;
int newLeft = postLayoutInfo == null ? disappearingItemView.getLeft() : postLayoutInfo.left;
int newTop = postLayoutInfo == null ? disappearingItemView.getTop() : postLayoutInfo.top;
if (!viewHolder.isRemoved() && (oldLeft != newLeft || oldTop != newTop)) {
disappearingItemView.layout(newLeft, newTop,
newLeft + disappearingItemView.getWidth(),
newTop + disappearingItemView.getHeight());
if (DEBUG) {
Log.d(TAG, "DISAPPEARING: " + viewHolder + " with view " + disappearingItemView);
}
return animateMove(viewHolder, oldLeft, oldTop, newLeft, newTop);
} else {
if (DEBUG) {
Log.d(TAG, "REMOVED: " + viewHolder + " with view " + disappearingItemView);
}
return animateRemove(viewHolder);
}
}
@Override
public boolean animateAppearance(@NonNull ViewHolder viewHolder,
@Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
if (preLayoutInfo != null && (preLayoutInfo.left != postLayoutInfo.left
|| preLayoutInfo.top != postLayoutInfo.top)) {
// slide items in if before/after locations differ
if (DEBUG) {
Log.d(TAG, "APPEARING: " + viewHolder + " with view " + viewHolder);
}
return animateMove(viewHolder, preLayoutInfo.left, preLayoutInfo.top,
postLayoutInfo.left, postLayoutInfo.top);
} else {
if (DEBUG) {
Log.d(TAG, "ADDED: " + viewHolder + " with view " + viewHolder);
}
return animateAdd(viewHolder);
}
}
@Override
public boolean animatePersistence(@NonNull ViewHolder viewHolder,
@NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
if (preInfo.left != postInfo.left || preInfo.top != postInfo.top) {
if (DEBUG) {
Log.d(TAG, "PERSISTENT: " + viewHolder +
" with view " + viewHolder.itemView);
}
return animateMove(viewHolder,
preInfo.left, preInfo.top, postInfo.left, postInfo.top);
}
dispatchMoveFinished(viewHolder);
return false;
}
@Override
public boolean animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
@NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
if (DEBUG) {
Log.d(TAG, "CHANGED: " + oldHolder + " with view " + oldHolder.itemView);
}
final int fromLeft = preInfo.left;
final int fromTop = preInfo.top;
final int toLeft, toTop;
if (newHolder.shouldIgnore()) {
toLeft = preInfo.left;
toTop = preInfo.top;
} else {
toLeft = postInfo.left;
toTop = postInfo.top;
}
return animateChange(oldHolder, newHolder, fromLeft, fromTop, toLeft, toTop);
}
/**
* Called when an item is removed from the RecyclerView. Implementors can choose
* whether and how to animate that change, but must always call
* {@link #dispatchRemoveFinished(ViewHolder)} when done, either
* immediately (if no animation will occur) or after the animation actually finishes.
* The return value indicates whether an animation has been set up and whether the
* ItemAnimator's {@link #runPendingAnimations()} method should be called at the
* next opportunity. This mechanism allows ItemAnimator to set up individual animations
* as separate calls to {@link #animateAdd(ViewHolder) animateAdd()},
* {@link #animateMove(ViewHolder, int, int, int, int) animateMove()},
* {@link #animateRemove(ViewHolder) animateRemove()}, and
* {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} come in one by one,
* then start the animations together in the later call to {@link #runPendingAnimations()}.
*
* <p>This method may also be called for disappearing items which continue to exist in the
* RecyclerView, but for which the system does not have enough information to animate
* them out of view. In that case, the default animation for removing items is run
* on those items as well.</p>
*
* @param holder The item that is being removed.
* @return true if a later call to {@link #runPendingAnimations()} is requested,
* false otherwise.
*/
abstract public boolean animateRemove(ViewHolder holder);
/**
* Called when an item is added to the RecyclerView. Implementors can choose
* whether and how to animate that change, but must always call
* {@link #dispatchAddFinished(ViewHolder)} when done, either
* immediately (if no animation will occur) or after the animation actually finishes.
* The return value indicates whether an animation has been set up and whether the
* ItemAnimator's {@link #runPendingAnimations()} method should be called at the
* next opportunity. This mechanism allows ItemAnimator to set up individual animations
* as separate calls to {@link #animateAdd(ViewHolder) animateAdd()},
* {@link #animateMove(ViewHolder, int, int, int, int) animateMove()},
* {@link #animateRemove(ViewHolder) animateRemove()}, and
* {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} come in one by one,
* then start the animations together in the later call to {@link #runPendingAnimations()}.
*
* <p>This method may also be called for appearing items which were already in the
* RecyclerView, but for which the system does not have enough information to animate
* them into view. In that case, the default animation for adding items is run
* on those items as well.</p>
*
* @param holder The item that is being added.
* @return true if a later call to {@link #runPendingAnimations()} is requested,
* false otherwise.
*/
abstract public boolean animateAdd(ViewHolder holder);
/**
* Called when an item is moved in the RecyclerView. Implementors can choose
* whether and how to animate that change, but must always call
* {@link #dispatchMoveFinished(ViewHolder)} when done, either
* immediately (if no animation will occur) or after the animation actually finishes.
* The return value indicates whether an animation has been set up and whether the
* ItemAnimator's {@link #runPendingAnimations()} method should be called at the
* next opportunity. This mechanism allows ItemAnimator to set up individual animations
* as separate calls to {@link #animateAdd(ViewHolder) animateAdd()},
* {@link #animateMove(ViewHolder, int, int, int, int) animateMove()},
* {@link #animateRemove(ViewHolder) animateRemove()}, and
* {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} come in one by one,
* then start the animations together in the later call to {@link #runPendingAnimations()}.
*
* @param holder The item that is being moved.
* @return true if a later call to {@link #runPendingAnimations()} is requested,
* false otherwise.
*/
abstract public boolean animateMove(ViewHolder holder, int fromX, int fromY,
int toX, int toY);
/**
* Called when an item is changed in the RecyclerView, as indicated by a call to
* {@link Adapter#notifyItemChanged(int)} or
* {@link Adapter#notifyItemRangeChanged(int, int)}.
* <p>
* Implementers can choose whether and how to animate changes, but must always call
* {@link #dispatchChangeFinished(ViewHolder, boolean)} for each non-null distinct ViewHolder,
* either immediately (if no animation will occur) or after the animation actually finishes.
* If the {@code oldHolder} is the same ViewHolder as the {@code newHolder}, you must call
* {@link #dispatchChangeFinished(ViewHolder, boolean)} once and only once. In that case, the
* second parameter of {@code dispatchChangeFinished} is ignored.
* <p>
* The return value indicates whether an animation has been set up and whether the
* ItemAnimator's {@link #runPendingAnimations()} method should be called at the
* next opportunity. This mechanism allows ItemAnimator to set up individual animations
* as separate calls to {@link #animateAdd(ViewHolder) animateAdd()},
* {@link #animateMove(ViewHolder, int, int, int, int) animateMove()},
* {@link #animateRemove(ViewHolder) animateRemove()}, and
* {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} come in one by one,
* then start the animations together in the later call to {@link #runPendingAnimations()}.
*
* @param oldHolder The original item that changed.
* @param newHolder The new item that was created with the changed content. Might be null
* @param fromLeft Left of the old view holder
* @param fromTop Top of the old view holder
* @param toLeft Left of the new view holder
* @param toTop Top of the new view holder
* @return true if a later call to {@link #runPendingAnimations()} is requested,
* false otherwise.
*/
abstract public boolean animateChange(ViewHolder oldHolder,
ViewHolder newHolder, int fromLeft, int fromTop, int toLeft, int toTop);
/**
* Method to be called by subclasses when a remove animation is done.
*
* @param item The item which has been removed
* @see RecyclerView.ItemAnimator#animateDisappearance(ViewHolder, ItemHolderInfo,
* ItemHolderInfo)
*/
public final void dispatchRemoveFinished(ViewHolder item) {
onRemoveFinished(item);
dispatchAnimationFinished(item);
}
/**
* Method to be called by subclasses when a move animation is done.
*
* @param item The item which has been moved
* @see RecyclerView.ItemAnimator#animateDisappearance(ViewHolder, ItemHolderInfo,
* ItemHolderInfo)
* @see RecyclerView.ItemAnimator#animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
* @see RecyclerView.ItemAnimator#animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
*/
public final void dispatchMoveFinished(ViewHolder item) {
onMoveFinished(item);
dispatchAnimationFinished(item);
}
/**
* Method to be called by subclasses when an add animation is done.
*
* @param item The item which has been added
*/
public final void dispatchAddFinished(ViewHolder item) {
onAddFinished(item);
dispatchAnimationFinished(item);
}
/**
* Method to be called by subclasses when a change animation is done.
*
* @param item The item which has been changed (this method must be called for
* each non-null ViewHolder passed into
* {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)}).
* @param oldItem true if this is the old item that was changed, false if
* it is the new item that replaced the old item.
* @see #animateChange(ViewHolder, ViewHolder, int, int, int, int)
*/
public final void dispatchChangeFinished(ViewHolder item, boolean oldItem) {
onChangeFinished(item, oldItem);
dispatchAnimationFinished(item);
}
/**
* Method to be called by subclasses when a remove animation is being started.
*
* @param item The item being removed
*/
public final void dispatchRemoveStarting(ViewHolder item) {
onRemoveStarting(item);
}
/**
* Method to be called by subclasses when a move animation is being started.
*
* @param item The item being moved
*/
public final void dispatchMoveStarting(ViewHolder item) {
onMoveStarting(item);
}
/**
* Method to be called by subclasses when an add animation is being started.
*
* @param item The item being added
*/
public final void dispatchAddStarting(ViewHolder item) {
onAddStarting(item);
}
/**
* Method to be called by subclasses when a change animation is being started.
*
* @param item The item which has been changed (this method must be called for
* each non-null ViewHolder passed into
* {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)}).
* @param oldItem true if this is the old item that was changed, false if
* it is the new item that replaced the old item.
*/
public final void dispatchChangeStarting(ViewHolder item, boolean oldItem) {
onChangeStarting(item, oldItem);
}
/**
* Called when a remove animation is being started on the given ViewHolder.
* The default implementation does nothing. Subclasses may wish to override
* this method to handle any ViewHolder-specific operations linked to animation
* lifecycles.
*
* @param item The ViewHolder being animated.
*/
@SuppressWarnings("UnusedParameters")
public void onRemoveStarting(ViewHolder item) {
}
/**
* Called when a remove animation has ended on the given ViewHolder.
* The default implementation does nothing. Subclasses may wish to override
* this method to handle any ViewHolder-specific operations linked to animation
* lifecycles.
*
* @param item The ViewHolder being animated.
*/
public void onRemoveFinished(ViewHolder item) {
}
/**
* Called when an add animation is being started on the given ViewHolder.
* The default implementation does nothing. Subclasses may wish to override
* this method to handle any ViewHolder-specific operations linked to animation
* lifecycles.
*
* @param item The ViewHolder being animated.
*/
@SuppressWarnings("UnusedParameters")
public void onAddStarting(ViewHolder item) {
}
/**
* Called when an add animation has ended on the given ViewHolder.
* The default implementation does nothing. Subclasses may wish to override
* this method to handle any ViewHolder-specific operations linked to animation
* lifecycles.
*
* @param item The ViewHolder being animated.
*/
public void onAddFinished(ViewHolder item) {
}
/**
* Called when a move animation is being started on the given ViewHolder.
* The default implementation does nothing. Subclasses may wish to override
* this method to handle any ViewHolder-specific operations linked to animation
* lifecycles.
*
* @param item The ViewHolder being animated.
*/
@SuppressWarnings("UnusedParameters")
public void onMoveStarting(ViewHolder item) {
}
/**
* Called when a move animation has ended on the given ViewHolder.
* The default implementation does nothing. Subclasses may wish to override
* this method to handle any ViewHolder-specific operations linked to animation
* lifecycles.
*
* @param item The ViewHolder being animated.
*/
public void onMoveFinished(ViewHolder item) {
}
/**
* Called when a change animation is being started on the given ViewHolder.
* The default implementation does nothing. Subclasses may wish to override
* this method to handle any ViewHolder-specific operations linked to animation
* lifecycles.
*
* @param item The ViewHolder being animated.
* @param oldItem true if this is the old item that was changed, false if
* it is the new item that replaced the old item.
*/
@SuppressWarnings("UnusedParameters")
public void onChangeStarting(ViewHolder item, boolean oldItem) {
}
/**
* Called when a change animation has ended on the given ViewHolder.
* The default implementation does nothing. Subclasses may wish to override
* this method to handle any ViewHolder-specific operations linked to animation
* lifecycles.
*
* @param item The ViewHolder being animated.
* @param oldItem true if this is the old item that was changed, false if
* it is the new item that replaced the old item.
*/
public void onChangeFinished(ViewHolder item, boolean oldItem) {
}
}

View File

@ -16,11 +16,18 @@
package org.telegram.messenger.support.widget;
import static org.telegram.messenger.support.widget.LayoutState.ITEM_DIRECTION_HEAD;
import static org.telegram.messenger.support.widget.LayoutState.ITEM_DIRECTION_TAIL;
import static org.telegram.messenger.support.widget.LayoutState.LAYOUT_END;
import static org.telegram.messenger.support.widget.LayoutState.LAYOUT_START;
import static org.telegram.messenger.support.widget.RecyclerView.NO_POSITION;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.accessibility.AccessibilityEventCompat;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
@ -31,24 +38,11 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import org.telegram.messenger.support.widget.AdapterHelper;
import org.telegram.messenger.support.widget.LayoutState;
import org.telegram.messenger.support.widget.LinearSmoothScroller;
import org.telegram.messenger.support.widget.OrientationHelper;
import org.telegram.messenger.support.widget.RecyclerView;
import org.telegram.messenger.support.widget.ScrollbarHelper;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.List;
import static org.telegram.messenger.support.widget.LayoutState.LAYOUT_START;
import static org.telegram.messenger.support.widget.LayoutState.LAYOUT_END;
import static org.telegram.messenger.support.widget.LayoutState.ITEM_DIRECTION_HEAD;
import static org.telegram.messenger.support.widget.LayoutState.ITEM_DIRECTION_TAIL;
import static org.telegram.messenger.support.widget.RecyclerView.NO_POSITION;
/**
* A LayoutManager that lays out children in a staggered grid formation.
* It supports horizontal & vertical layout as well as an ability to layout children in reverse.
@ -72,6 +66,7 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
*/
public static final int GAP_HANDLING_NONE = 0;
@SuppressWarnings("unused")
@Deprecated
public static final int GAP_HANDLING_LAZY = 1;
@ -97,6 +92,12 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
public static final int GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS = 2;
private static final int INVALID_OFFSET = Integer.MIN_VALUE;
/**
* While trying to find next view to focus, LayoutManager will not try to scroll more
* than this factor times the total space of the list. If layout is vertical, total space is the
* height minus padding, if layout is horizontal, total space is the width minus padding.
*/
private static final float MAX_SCROLL_FACTOR = 1 / 3f;
/**
* Number of spans
@ -175,7 +176,7 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
/**
* Re-used measurement specs. updated by onLayout.
*/
private int mFullSizeSpec, mWidthSpec, mHeightSpec;
private int mFullSizeSpec;
/**
* Re-used rectangle to get child decor offsets.
@ -208,6 +209,20 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
}
};
/**
* Constructor used when layout manager is set in XML by RecyclerView attribute
* "layoutManager". Defaults to single column and vertical.
*/
@SuppressWarnings("unused")
public StaggeredGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
Properties properties = getProperties(context, attrs, defStyleAttr, defStyleRes);
setOrientation(properties.orientation);
setSpanCount(properties.spanCount);
setReverseLayout(properties.reverseLayout);
setAutoMeasureEnabled(mGapStrategy != GAP_HANDLING_NONE);
}
/**
* Creates a StaggeredGridLayoutManager with given parameters.
*
@ -218,6 +233,7 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
public StaggeredGridLayoutManager(int spanCount, int orientation) {
mOrientation = orientation;
setSpanCount(spanCount);
setAutoMeasureEnabled(mGapStrategy != GAP_HANDLING_NONE);
}
/**
@ -358,10 +374,16 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
private boolean checkSpanForGap(Span span) {
if (mShouldReverseLayout) {
if (span.getEndLine() < mPrimaryOrientation.getEndAfterPadding()) {
return true;
// if it is full span, it is OK
final View endView = span.mViews.get(span.mViews.size() - 1);
final LayoutParams lp = span.getLayoutParams(endView);
return !lp.mFullSpan;
}
} else if (span.getStartLine() > mPrimaryOrientation.getStartAfterPadding()) {
return true;
// if it is full span, it is OK
final View startView = span.mViews.get(0);
final LayoutParams lp = span.getLayoutParams(startView);
return !lp.mFullSpan;
}
return false;
}
@ -473,6 +495,7 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
+ "or GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS");
}
mGapStrategy = gapStrategy;
setAutoMeasureEnabled(mGapStrategy != GAP_HANDLING_NONE);
requestLayout();
}
@ -541,8 +564,35 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
public boolean getReverseLayout() {
return mReverseLayout;
}
@Override
public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
// we don't like it to wrap content in our non-scroll direction.
final int width, height;
final int horizontalPadding = getPaddingLeft() + getPaddingRight();
final int verticalPadding = getPaddingTop() + getPaddingBottom();
if (mOrientation == VERTICAL) {
final int usedHeight = childrenBounds.height() + verticalPadding;
height = chooseSize(hSpec, usedHeight, getMinimumHeight());
width = chooseSize(wSpec, mSizePerSpan * mSpanCount + horizontalPadding,
getMinimumWidth());
} else {
final int usedWidth = childrenBounds.width() + horizontalPadding;
width = chooseSize(wSpec, usedWidth, getMinimumWidth());
height = chooseSize(hSpec, mSizePerSpan * mSpanCount + verticalPadding,
getMinimumHeight());
}
setMeasuredDimension(width, height);
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
onLayoutChildren(recycler, state, true);
}
private void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state,
boolean shouldCheckForGaps) {
ensureOrientationHelper();
final AnchorInfo anchorInfo = mAnchorInfo;
anchorInfo.reset();
@ -588,8 +638,9 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
}
}
detachAndScrapAttachedViews(recycler);
mLayoutState.mRecycle = false;
mLaidOutInvalidFullSpan = false;
updateMeasureSpecs();
updateMeasureSpecs(mSecondaryOrientation.getTotalSpace());
updateLayoutState(anchorInfo.mPosition, state);
if (anchorInfo.mLayoutFromEnd) {
// Layout start.
@ -609,6 +660,8 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
fill(recycler, mLayoutState, state);
}
repositionToWrapContentIfNecessary();
if (getChildCount() > 0) {
if (mShouldReverseLayout) {
fixEndGap(recycler, state, true);
@ -618,14 +671,16 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
fixEndGap(recycler, state, false);
}
}
if (!state.isPreLayout()) {
boolean hasGaps = false;
if (shouldCheckForGaps && !state.isPreLayout()) {
final boolean needToCheckForGaps = mGapStrategy != GAP_HANDLING_NONE
&& getChildCount() > 0
&& (mLaidOutInvalidFullSpan || hasGapsToFix() != null);
if (needToCheckForGaps) {
removeCallbacks(mCheckForGapsRunnable);
postOnAnimation(mCheckForGapsRunnable);
if (checkForGaps()) {
hasGaps = true;
}
}
mPendingScrollPosition = NO_POSITION;
mPendingScrollPositionOffset = INVALID_OFFSET;
@ -633,6 +688,58 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
mLastLayoutFromEnd = anchorInfo.mLayoutFromEnd;
mLastLayoutRTL = isLayoutRTL();
mPendingSavedState = null; // we don't need this anymore
if (hasGaps) {
onLayoutChildren(recycler, state, false);
}
}
private void repositionToWrapContentIfNecessary() {
if (mSecondaryOrientation.getMode() == View.MeasureSpec.EXACTLY) {
return; // nothing to do
}
float maxSize = 0;
final int childCount = getChildCount();
for (int i = 0; i < childCount; i ++) {
View child = getChildAt(i);
float size = mSecondaryOrientation.getDecoratedMeasurement(child);
if (size < maxSize) {
continue;
}
LayoutParams layoutParams = (LayoutParams) child.getLayoutParams();
if (layoutParams.isFullSpan()) {
size = 1f * size / mSpanCount;
}
maxSize = Math.max(maxSize, size);
}
int before = mSizePerSpan;
int desired = Math.round(maxSize * mSpanCount);
if (mSecondaryOrientation.getMode() == View.MeasureSpec.AT_MOST) {
desired = Math.min(desired, mSecondaryOrientation.getTotalSpace());
}
updateMeasureSpecs(desired);
if (mSizePerSpan == before) {
return; // nothing has changed
}
for (int i = 0; i < childCount; i ++) {
View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp.mFullSpan) {
continue;
}
if (isLayoutRTL() && mOrientation == VERTICAL) {
int newOffset = -(mSpanCount - 1 - lp.mSpan.mIndex) * mSizePerSpan;
int prevOffset = -(mSpanCount - 1 - lp.mSpan.mIndex) * before;
child.offsetLeftAndRight(newOffset - prevOffset);
} else {
int newOffset = lp.mSpan.mIndex * mSizePerSpan;
int prevOffset = lp.mSpan.mIndex * before;
if (mOrientation == VERTICAL) {
child.offsetLeftAndRight(newOffset - prevOffset);
} else {
child.offsetTopAndBottom(newOffset - prevOffset);
}
}
}
}
private void applyPendingSavedState(AnchorInfo anchorInfo) {
@ -780,17 +887,11 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
return true;
}
void updateMeasureSpecs() {
mSizePerSpan = mSecondaryOrientation.getTotalSpace() / mSpanCount;
void updateMeasureSpecs(int totalSpace) {
mSizePerSpan = totalSpace / mSpanCount;
//noinspection ResourceType
mFullSizeSpec = View.MeasureSpec.makeMeasureSpec(
mSecondaryOrientation.getTotalSpace(), View.MeasureSpec.EXACTLY);
if (mOrientation == VERTICAL) {
mWidthSpec = View.MeasureSpec.makeMeasureSpec(mSizePerSpan, View.MeasureSpec.EXACTLY);
mHeightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
} else {
mHeightSpec = View.MeasureSpec.makeMeasureSpec(mSizePerSpan, View.MeasureSpec.EXACTLY);
mWidthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
}
totalSpace, mSecondaryOrientation.getMode());
}
@Override
@ -989,43 +1090,48 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
return computeScrollRange(state);
}
private void measureChildWithDecorationsAndMargin(View child, LayoutParams lp) {
private void measureChildWithDecorationsAndMargin(View child, LayoutParams lp,
boolean alreadyMeasured) {
if (lp.mFullSpan) {
if (mOrientation == VERTICAL) {
measureChildWithDecorationsAndMargin(child, mFullSizeSpec,
getSpecForDimension(lp.height, mHeightSpec));
getChildMeasureSpec(getHeight(), getHeightMode(), 0, lp.height, true),
alreadyMeasured);
} else {
measureChildWithDecorationsAndMargin(child,
getSpecForDimension(lp.width, mWidthSpec), mFullSizeSpec);
getChildMeasureSpec(getWidth(), getWidthMode(), 0, lp.width, true),
mFullSizeSpec, alreadyMeasured);
}
} else {
if (mOrientation == VERTICAL) {
measureChildWithDecorationsAndMargin(child, mWidthSpec,
getSpecForDimension(lp.height, mHeightSpec));
measureChildWithDecorationsAndMargin(child,
getChildMeasureSpec(mSizePerSpan, getWidthMode(), 0, lp.width, false),
getChildMeasureSpec(getHeight(), getHeightMode(), 0, lp.height, true),
alreadyMeasured);
} else {
measureChildWithDecorationsAndMargin(child,
getSpecForDimension(lp.width, mWidthSpec), mHeightSpec);
getChildMeasureSpec(getWidth(), getWidthMode(), 0, lp.width, true),
getChildMeasureSpec(mSizePerSpan, getHeightMode(), 0, lp.height, false),
alreadyMeasured);
}
}
}
private int getSpecForDimension(int dim, int defaultSpec) {
if (dim < 0) {
return defaultSpec;
} else {
return View.MeasureSpec.makeMeasureSpec(dim, View.MeasureSpec.EXACTLY);
}
}
private void measureChildWithDecorationsAndMargin(View child, int widthSpec,
int heightSpec) {
int heightSpec, boolean alreadyMeasured) {
calculateItemDecorationsForChild(child, mTmpRect);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
widthSpec = updateSpecWithExtra(widthSpec, lp.leftMargin + mTmpRect.left,
lp.rightMargin + mTmpRect.right);
heightSpec = updateSpecWithExtra(heightSpec, lp.topMargin + mTmpRect.top,
lp.bottomMargin + mTmpRect.bottom);
child.measure(widthSpec, heightSpec);
final boolean measure = alreadyMeasured
? shouldReMeasureChild(child, widthSpec, heightSpec, lp)
: shouldMeasureChild(child, widthSpec, heightSpec, lp);
if (measure) {
child.measure(widthSpec, heightSpec);
}
}
private int updateSpecWithExtra(int spec, int startInset, int endInset) {
@ -1035,7 +1141,7 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
final int mode = View.MeasureSpec.getMode(spec);
if (mode == View.MeasureSpec.AT_MOST || mode == View.MeasureSpec.EXACTLY) {
return View.MeasureSpec.makeMeasureSpec(
View.MeasureSpec.getSize(spec) - startInset - endInset, mode);
Math.max(0, View.MeasureSpec.getSize(spec) - startInset - endInset), mode);
}
return spec;
}
@ -1238,7 +1344,10 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
private void fixEndGap(RecyclerView.Recycler recycler, RecyclerView.State state,
boolean canOffsetChildren) {
final int maxEndLine = getMaxEnd(mPrimaryOrientation.getEndAfterPadding());
final int maxEndLine = getMaxEnd(Integer.MIN_VALUE);
if (maxEndLine == Integer.MIN_VALUE) {
return;
}
int gap = mPrimaryOrientation.getEndAfterPadding() - maxEndLine;
int fixOffset;
if (gap > 0) {
@ -1254,7 +1363,10 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
private void fixStartGap(RecyclerView.Recycler recycler, RecyclerView.State state,
boolean canOffsetChildren) {
final int minStartLine = getMinStart(mPrimaryOrientation.getStartAfterPadding());
final int minStartLine = getMinStart(Integer.MAX_VALUE);
if (minStartLine == Integer.MAX_VALUE) {
return;
}
int gap = minStartLine - mPrimaryOrientation.getStartAfterPadding();
int fixOffset;
if (gap > 0) {
@ -1293,6 +1405,9 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
mLayoutState.mEndLine = mPrimaryOrientation.getEnd() + endExtra;
mLayoutState.mStartLine = -startExtra;
}
mLayoutState.mStopInFocusable = false;
mLayoutState.mRecycle = true;
mLayoutState.mInfinite = mPrimaryOrientation.getMode() == View.MeasureSpec.UNSPECIFIED;
}
private void setLayoutStateDirection(int direction) {
@ -1397,10 +1512,18 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
final int targetLine;
// Line of the furthest row.
if (layoutState.mLayoutDirection == LAYOUT_END) {
targetLine = layoutState.mEndLine + layoutState.mAvailable;
} else { // LAYOUT_START
targetLine = layoutState.mStartLine - layoutState.mAvailable;
if (mLayoutState.mInfinite) {
if (layoutState.mLayoutDirection == LAYOUT_END) {
targetLine = Integer.MAX_VALUE;
} else { // LAYOUT_START
targetLine = Integer.MIN_VALUE;
}
} else {
if (layoutState.mLayoutDirection == LAYOUT_END) {
targetLine = layoutState.mEndLine + layoutState.mAvailable;
} else { // LAYOUT_START
targetLine = layoutState.mStartLine - layoutState.mAvailable;
}
}
updateAllRemainingSpans(layoutState.mLayoutDirection, targetLine);
@ -1414,7 +1537,8 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
? mPrimaryOrientation.getEndAfterPadding()
: mPrimaryOrientation.getStartAfterPadding();
boolean added = false;
while (layoutState.hasMore(state) && !mRemainingSpans.isEmpty()) {
while (layoutState.hasMore(state)
&& (mLayoutState.mInfinite || !mRemainingSpans.isEmpty())) {
View view = layoutState.next(recycler);
LayoutParams lp = ((LayoutParams) view.getLayoutParams());
final int position = lp.getViewLayoutPosition();
@ -1440,7 +1564,7 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
} else {
addView(view, 0);
}
measureChildWithDecorationsAndMargin(view, lp);
measureChildWithDecorationsAndMargin(view, lp, false);
final int start;
final int end;
@ -1488,13 +1612,22 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
mLaidOutInvalidFullSpan = true;
}
}
}
attachViewToSpans(view, lp, layoutState);
final int otherStart = lp.mFullSpan ? mSecondaryOrientation.getStartAfterPadding()
: currentSpan.mIndex * mSizePerSpan +
mSecondaryOrientation.getStartAfterPadding();
final int otherEnd = otherStart + mSecondaryOrientation.getDecoratedMeasurement(view);
final int otherStart;
final int otherEnd;
if (isLayoutRTL() && mOrientation == VERTICAL) {
otherEnd = lp.mFullSpan ? mSecondaryOrientation.getEndAfterPadding() :
mSecondaryOrientation.getEndAfterPadding()
- (mSpanCount - 1 - currentSpan.mIndex) * mSizePerSpan;
otherStart = otherEnd - mSecondaryOrientation.getDecoratedMeasurement(view);
} else {
otherStart = lp.mFullSpan ? mSecondaryOrientation.getStartAfterPadding()
: currentSpan.mIndex * mSizePerSpan +
mSecondaryOrientation.getStartAfterPadding();
otherEnd = otherStart + mSecondaryOrientation.getDecoratedMeasurement(view);
}
if (mOrientation == VERTICAL) {
layoutDecoratedWithMargins(view, otherStart, start, otherEnd, end);
} else {
@ -1507,6 +1640,13 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
updateRemainingSpans(currentSpan, mLayoutState.mLayoutDirection, targetLine);
}
recycle(recycler, mLayoutState);
if (mLayoutState.mStopInFocusable && view.isFocusable()) {
if (lp.mFullSpan) {
mRemainingSpans.clear();
} else {
mRemainingSpans.set(currentSpan.mIndex, false);
}
}
added = true;
}
if (!added) {
@ -1558,6 +1698,9 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
}
private void recycle(RecyclerView.Recycler recycler, LayoutState layoutState) {
if (!layoutState.mRecycle || layoutState.mInfinite) {
return;
}
if (layoutState.mAvailable == 0) {
// easy, recycle line is still valid
if (layoutState.mLayoutDirection == LAYOUT_START) {
@ -1913,6 +2056,7 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
layoutDir = LAYOUT_START;
referenceChildPosition = getFirstChildPosition();
}
mLayoutState.mRecycle = true;
updateLayoutState(referenceChildPosition, state);
setLayoutStateDirection(layoutDir);
mLayoutState.mCurrentPosition = referenceChildPosition + mLayoutState.mItemDirection;
@ -1982,8 +2126,13 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
if (mOrientation == HORIZONTAL) {
return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.FILL_PARENT);
} else {
return new LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
}
@Override
@ -2009,6 +2158,105 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
return mOrientation;
}
@Nullable
@Override
public View onFocusSearchFailed(View focused, int direction, RecyclerView.Recycler recycler,
RecyclerView.State state) {
if (getChildCount() == 0) {
return null;
}
final View directChild = findContainingItemView(focused);
if (directChild == null) {
return null;
}
ensureOrientationHelper();
resolveShouldLayoutReverse();
final int layoutDir = convertFocusDirectionToLayoutDirection(direction);
if (layoutDir == LayoutState.INVALID_LAYOUT) {
return null;
}
LayoutParams prevFocusLayoutParams = (LayoutParams) directChild.getLayoutParams();
boolean prevFocusFullSpan = prevFocusLayoutParams.mFullSpan;
final Span prevFocusSpan = prevFocusLayoutParams.mSpan;
final int referenceChildPosition;
if (layoutDir == LAYOUT_END) { // layout towards end
referenceChildPosition = getLastChildPosition();
} else {
referenceChildPosition = getFirstChildPosition();
}
updateLayoutState(referenceChildPosition, state);
setLayoutStateDirection(layoutDir);
mLayoutState.mCurrentPosition = referenceChildPosition + mLayoutState.mItemDirection;
mLayoutState.mAvailable = (int) (MAX_SCROLL_FACTOR * mPrimaryOrientation.getTotalSpace());
mLayoutState.mStopInFocusable = true;
mLayoutState.mRecycle = false;
fill(recycler, mLayoutState, state);
mLastLayoutFromEnd = mShouldReverseLayout;
if (!prevFocusFullSpan) {
View view = prevFocusSpan.getFocusableViewAfter(referenceChildPosition, layoutDir);
if (view != null && view != directChild) {
return view;
}
}
// either could not find from the desired span or prev view is full span.
// traverse all spans
if (preferLastSpan(layoutDir)) {
for (int i = mSpanCount - 1; i >= 0; i --) {
View view = mSpans[i].getFocusableViewAfter(referenceChildPosition, layoutDir);
if (view != null && view != directChild) {
return view;
}
}
} else {
for (int i = 0; i < mSpanCount; i ++) {
View view = mSpans[i].getFocusableViewAfter(referenceChildPosition, layoutDir);
if (view != null && view != directChild) {
return view;
}
}
}
return null;
}
/**
* Converts a focusDirection to orientation.
*
* @param focusDirection One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
* {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
* {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
* or 0 for not applicable
* @return {@link LayoutState#LAYOUT_START} or {@link LayoutState#LAYOUT_END} if focus direction
* is applicable to current state, {@link LayoutState#INVALID_LAYOUT} otherwise.
*/
private int convertFocusDirectionToLayoutDirection(int focusDirection) {
switch (focusDirection) {
case View.FOCUS_BACKWARD:
return LayoutState.LAYOUT_START;
case View.FOCUS_FORWARD:
return LayoutState.LAYOUT_END;
case View.FOCUS_UP:
return mOrientation == VERTICAL ? LayoutState.LAYOUT_START
: LayoutState.INVALID_LAYOUT;
case View.FOCUS_DOWN:
return mOrientation == VERTICAL ? LayoutState.LAYOUT_END
: LayoutState.INVALID_LAYOUT;
case View.FOCUS_LEFT:
return mOrientation == HORIZONTAL ? LayoutState.LAYOUT_START
: LayoutState.INVALID_LAYOUT;
case View.FOCUS_RIGHT:
return mOrientation == HORIZONTAL ? LayoutState.LAYOUT_END
: LayoutState.INVALID_LAYOUT;
default:
if (DEBUG) {
Log.d(TAG, "Unknown focus request:" + focusDirection);
}
return LayoutState.INVALID_LAYOUT;
}
}
/**
* LayoutParams used by StaggeredGridLayoutManager.
@ -2089,7 +2337,7 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
class Span {
static final int INVALID_LINE = Integer.MIN_VALUE;
private ArrayList<View> mViews = new ArrayList<View>();
private ArrayList<View> mViews = new ArrayList<>();
int mCachedStart = INVALID_LINE;
int mCachedEnd = INVALID_LINE;
int mDeletedSize = 0;
@ -2273,45 +2521,6 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
}
}
// normalized offset is how much this span can scroll
int getNormalizedOffset(int dt, int targetStart, int targetEnd) {
if (mViews.size() == 0) {
return 0;
}
if (dt < 0) {
final int endSpace = getEndLine() - targetEnd;
if (endSpace <= 0) {
return 0;
}
return -dt > endSpace ? -endSpace : dt;
} else {
final int startSpace = targetStart - getStartLine();
if (startSpace <= 0) {
return 0;
}
return startSpace < dt ? startSpace : dt;
}
}
/**
* Returns if there is no child between start-end lines
*
* @param start The start line
* @param end The end line
* @return true if a new child can be added between start and end
*/
boolean isEmpty(int start, int end) {
final int count = mViews.size();
for (int i = 0; i < count; i++) {
final View view = mViews.get(i);
if (mPrimaryOrientation.getDecoratedStart(view) < end &&
mPrimaryOrientation.getDecoratedEnd(view) > start) {
return false;
}
}
return true;
}
public int findFirstVisibleItemPosition() {
return mReverseLayout
? findOneVisibleChild(mViews.size() - 1, -1, false)
@ -2356,6 +2565,36 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
}
return NO_POSITION;
}
/**
* Depending on the layout direction, returns the View that is after the given position.
*/
public View getFocusableViewAfter(int referenceChildPosition, int layoutDir) {
View candidate = null;
if (layoutDir == LAYOUT_START) {
final int limit = mViews.size();
for (int i = 0; i < limit; i++) {
final View view = mViews.get(i);
if (view.isFocusable() &&
(getPosition(view) > referenceChildPosition == mReverseLayout) ) {
candidate = view;
} else {
break;
}
}
} else {
for (int i = mViews.size() - 1; i >= 0; i--) {
final View view = mViews.get(i);
if (view.isFocusable() &&
(getPosition(view) > referenceChildPosition == !mReverseLayout)) {
candidate = view;
} else {
break;
}
}
}
return candidate;
}
}
/**
@ -2532,7 +2771,7 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
public void addFullSpanItem(FullSpanItem fullSpanItem) {
if (mFullSpanItems == null) {
mFullSpanItems = new ArrayList<FullSpanItem>();
mFullSpanItems = new ArrayList<>();
}
final int size = mFullSpanItems.size();
for (int i = 0; i < size; i++) {
@ -2624,10 +2863,6 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
return mGapPerSpan == null ? 0 : mGapPerSpan[spanIndex];
}
public void invalidateSpanGaps() {
mGapPerSpan = null;
}
@Override
public int describeContents() {
return 0;
@ -2671,7 +2906,10 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
}
}
static class SavedState implements Parcelable {
/**
* @hide
*/
public static class SavedState implements Parcelable {
int mAnchorPosition;
int mVisibleAnchorPosition; // Replacement for span info when spans are invalidated
@ -2704,6 +2942,7 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
mReverseLayout = in.readInt() == 1;
mAnchorLayoutFromEnd = in.readInt() == 1;
mLastLayoutRTL = in.readInt() == 1;
//noinspection unchecked
mFullSpanItems = in.readArrayList(
LazySpanLookup.FullSpanItem.class.getClassLoader());
}

View File

@ -0,0 +1,326 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.telegram.messenger.support.widget;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.support.v4.util.ArrayMap;
import android.support.v4.util.LongSparseArray;
import android.support.v4.util.Pools;
import static org.telegram.messenger.support.widget.RecyclerView.ViewHolder;
import static org.telegram.messenger.support.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
import static org.telegram.messenger.support.widget.ViewInfoStore.InfoRecord.FLAG_APPEAR_PRE_AND_POST;
import static org.telegram.messenger.support.widget.ViewInfoStore.InfoRecord.FLAG_APPEAR_AND_DISAPPEAR;
import static org.telegram.messenger.support.widget.ViewInfoStore.InfoRecord.FLAG_PRE_AND_POST;
import static org.telegram.messenger.support.widget.ViewInfoStore.InfoRecord.FLAG_DISAPPEARED;
import static org.telegram.messenger.support.widget.ViewInfoStore.InfoRecord.FLAG_APPEAR;
import static org.telegram.messenger.support.widget.ViewInfoStore.InfoRecord.FLAG_PRE;
import static org.telegram.messenger.support.widget.ViewInfoStore.InfoRecord.FLAG_POST;
class ViewInfoStore {
private static final boolean DEBUG = false;
/**
* View data records for pre-layout
*/
@VisibleForTesting
final ArrayMap<ViewHolder, InfoRecord> mLayoutHolderMap = new ArrayMap<>();
@VisibleForTesting
final LongSparseArray<ViewHolder> mOldChangedHolders = new LongSparseArray<>();
/**
* Clears the state and all existing tracking data
*/
void clear() {
mLayoutHolderMap.clear();
mOldChangedHolders.clear();
}
/**
* Adds the item information to the prelayout tracking
* @param holder The ViewHolder whose information is being saved
* @param info The information to save
*/
void addToPreLayout(ViewHolder holder, ItemHolderInfo info) {
InfoRecord record = mLayoutHolderMap.get(holder);
if (record == null) {
record = InfoRecord.obtain();
mLayoutHolderMap.put(holder, record);
}
record.preInfo = info;
record.flags |= FLAG_PRE;
}
boolean isDisappearing(ViewHolder holder) {
final InfoRecord record = mLayoutHolderMap.get(holder);
return record != null && ((record.flags & FLAG_DISAPPEARED) != 0);
}
/**
* Finds the ItemHolderInfo for the given ViewHolder in preLayout list and removes it.
*
* @param vh The ViewHolder whose information is being queried
* @return The ItemHolderInfo for the given ViewHolder or null if it does not exist
*/
@Nullable
ItemHolderInfo popFromPreLayout(ViewHolder vh) {
return popFromLayoutStep(vh, FLAG_PRE);
}
/**
* Finds the ItemHolderInfo for the given ViewHolder in postLayout list and removes it.
*
* @param vh The ViewHolder whose information is being queried
* @return The ItemHolderInfo for the given ViewHolder or null if it does not exist
*/
@Nullable
ItemHolderInfo popFromPostLayout(ViewHolder vh) {
return popFromLayoutStep(vh, FLAG_POST);
}
private ItemHolderInfo popFromLayoutStep(ViewHolder vh, int flag) {
int index = mLayoutHolderMap.indexOfKey(vh);
if (index < 0) {
return null;
}
final InfoRecord record = mLayoutHolderMap.valueAt(index);
if (record != null && (record.flags & flag) != 0) {
record.flags &= ~flag;
final ItemHolderInfo info;
if (flag == FLAG_PRE) {
info = record.preInfo;
} else if (flag == FLAG_POST) {
info = record.postInfo;
} else {
throw new IllegalArgumentException("Must provide flag PRE or POST");
}
// if not pre-post flag is left, clear.
if ((record.flags & (FLAG_PRE | FLAG_POST)) == 0) {
mLayoutHolderMap.removeAt(index);
InfoRecord.recycle(record);
}
return info;
}
return null;
}
/**
* Adds the given ViewHolder to the oldChangeHolders list
* @param key The key to identify the ViewHolder.
* @param holder The ViewHolder to store
*/
void addToOldChangeHolders(long key, ViewHolder holder) {
mOldChangedHolders.put(key, holder);
}
/**
* Adds the given ViewHolder to the appeared in pre layout list. These are Views added by the
* LayoutManager during a pre-layout pass. We distinguish them from other views that were
* already in the pre-layout so that ItemAnimator can choose to run a different animation for
* them.
*
* @param holder The ViewHolder to store
* @param info The information to save
*/
void addToAppearedInPreLayoutHolders(ViewHolder holder, ItemHolderInfo info) {
InfoRecord record = mLayoutHolderMap.get(holder);
if (record == null) {
record = InfoRecord.obtain();
mLayoutHolderMap.put(holder, record);
}
record.flags |= FLAG_APPEAR;
record.preInfo = info;
}
/**
* Checks whether the given ViewHolder is in preLayout list
* @param viewHolder The ViewHolder to query
*
* @return True if the ViewHolder is present in preLayout, false otherwise
*/
boolean isInPreLayout(ViewHolder viewHolder) {
final InfoRecord record = mLayoutHolderMap.get(viewHolder);
return record != null && (record.flags & FLAG_PRE) != 0;
}
/**
* Queries the oldChangeHolder list for the given key. If they are not tracked, simply returns
* null.
* @param key The key to be used to find the ViewHolder.
*
* @return A ViewHolder if exists or null if it does not exist.
*/
ViewHolder getFromOldChangeHolders(long key) {
return mOldChangedHolders.get(key);
}
/**
* Adds the item information to the post layout list
* @param holder The ViewHolder whose information is being saved
* @param info The information to save
*/
void addToPostLayout(ViewHolder holder, ItemHolderInfo info) {
InfoRecord record = mLayoutHolderMap.get(holder);
if (record == null) {
record = InfoRecord.obtain();
mLayoutHolderMap.put(holder, record);
}
record.postInfo = info;
record.flags |= FLAG_POST;
}
/**
* A ViewHolder might be added by the LayoutManager just to animate its disappearance.
* This list holds such items so that we can animate / recycle these ViewHolders properly.
*
* @param holder The ViewHolder which disappeared during a layout.
*/
void addToDisappearedInLayout(ViewHolder holder) {
InfoRecord record = mLayoutHolderMap.get(holder);
if (record == null) {
record = InfoRecord.obtain();
mLayoutHolderMap.put(holder, record);
}
record.flags |= FLAG_DISAPPEARED;
}
/**
* Removes a ViewHolder from disappearing list.
* @param holder The ViewHolder to be removed from the disappearing list.
*/
void removeFromDisappearedInLayout(ViewHolder holder) {
InfoRecord record = mLayoutHolderMap.get(holder);
if (record == null) {
return;
}
record.flags &= ~FLAG_DISAPPEARED;
}
void process(ProcessCallback callback) {
for (int index = mLayoutHolderMap.size() - 1; index >= 0; index --) {
final ViewHolder viewHolder = mLayoutHolderMap.keyAt(index);
final InfoRecord record = mLayoutHolderMap.removeAt(index);
if ((record.flags & FLAG_APPEAR_AND_DISAPPEAR) == FLAG_APPEAR_AND_DISAPPEAR) {
// Appeared then disappeared. Not useful for animations.
callback.unused(viewHolder);
} else if ((record.flags & FLAG_DISAPPEARED) != 0) {
// Set as "disappeared" by the LayoutManager (addDisappearingView)
if (record.preInfo == null) {
// similar to appear disappear but happened between different layout passes.
// this can happen when the layout manager is using auto-measure
callback.unused(viewHolder);
} else {
callback.processDisappeared(viewHolder, record.preInfo, record.postInfo);
}
} else if ((record.flags & FLAG_APPEAR_PRE_AND_POST) == FLAG_APPEAR_PRE_AND_POST) {
// Appeared in the layout but not in the adapter (e.g. entered the viewport)
callback.processAppeared(viewHolder, record.preInfo, record.postInfo);
} else if ((record.flags & FLAG_PRE_AND_POST) == FLAG_PRE_AND_POST) {
// Persistent in both passes. Animate persistence
callback.processPersistent(viewHolder, record.preInfo, record.postInfo);
} else if ((record.flags & FLAG_PRE) != 0) {
// Was in pre-layout, never been added to post layout
callback.processDisappeared(viewHolder, record.preInfo, null);
} else if ((record.flags & FLAG_POST) != 0) {
// Was not in pre-layout, been added to post layout
callback.processAppeared(viewHolder, record.preInfo, record.postInfo);
} else if ((record.flags & FLAG_APPEAR) != 0) {
// Scrap view. RecyclerView will handle removing/recycling this.
} else if (DEBUG) {
throw new IllegalStateException("record without any reasonable flag combination:/");
}
InfoRecord.recycle(record);
}
}
/**
* Removes the ViewHolder from all list
* @param holder The ViewHolder which we should stop tracking
*/
void removeViewHolder(ViewHolder holder) {
for (int i = mOldChangedHolders.size() - 1; i >= 0; i--) {
if (holder == mOldChangedHolders.valueAt(i)) {
mOldChangedHolders.removeAt(i);
break;
}
}
final InfoRecord info = mLayoutHolderMap.remove(holder);
if (info != null) {
InfoRecord.recycle(info);
}
}
void onDetach() {
InfoRecord.drainCache();
}
public void onViewDetached(ViewHolder viewHolder) {
removeFromDisappearedInLayout(viewHolder);
}
interface ProcessCallback {
void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo preInfo,
@Nullable ItemHolderInfo postInfo);
void processAppeared(ViewHolder viewHolder, @Nullable ItemHolderInfo preInfo,
ItemHolderInfo postInfo);
void processPersistent(ViewHolder viewHolder, @NonNull ItemHolderInfo preInfo,
@NonNull ItemHolderInfo postInfo);
void unused(ViewHolder holder);
}
static class InfoRecord {
// disappearing list
static final int FLAG_DISAPPEARED = 1;
// appear in pre layout list
static final int FLAG_APPEAR = 1 << 1;
// pre layout, this is necessary to distinguish null item info
static final int FLAG_PRE = 1 << 2;
// post layout, this is necessary to distinguish null item info
static final int FLAG_POST = 1 << 3;
static final int FLAG_APPEAR_AND_DISAPPEAR = FLAG_APPEAR | FLAG_DISAPPEARED;
static final int FLAG_PRE_AND_POST = FLAG_PRE | FLAG_POST;
static final int FLAG_APPEAR_PRE_AND_POST = FLAG_APPEAR | FLAG_PRE | FLAG_POST;
int flags;
@Nullable ItemHolderInfo preInfo;
@Nullable ItemHolderInfo postInfo;
static Pools.Pool<InfoRecord> sPool = new Pools.SimplePool<>(20);
private InfoRecord() {
}
static InfoRecord obtain() {
InfoRecord record = sPool.acquire();
return record == null ? new InfoRecord() : record;
}
static void recycle(InfoRecord record) {
record.flags = 0;
record.preInfo = null;
record.postInfo = null;
sPool.release(record);
}
static void drainCache() {
//noinspection StatementWithEmptyBody
while (sPool.acquire() != null);
}
}
}

View File

@ -16,13 +16,15 @@
package org.telegram.messenger.support.widget.helper;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Build;
import android.support.v4.animation.ValueAnimatorCompat;
import android.support.annotation.Nullable;
import android.support.v4.animation.AnimatorCompatHelper;
import android.support.v4.animation.AnimatorListenerCompat;
import android.support.v4.animation.AnimatorUpdateListenerCompat;
import android.support.v4.animation.ValueAnimatorCompat;
import android.support.v4.view.GestureDetectorCompat;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.VelocityTrackerCompat;
@ -31,6 +33,8 @@ import android.support.v4.view.ViewCompat;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.support.widget.LinearLayoutManager;
import org.telegram.messenger.support.widget.RecyclerView;
import org.telegram.messenger.support.widget.RecyclerView.OnItemTouchListener;
import org.telegram.messenger.support.widget.RecyclerView.ViewHolder;
import android.util.Log;
import android.view.GestureDetector;
import android.view.HapticFeedbackConstants;
@ -39,14 +43,11 @@ import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewParent;
import android.view.animation.Interpolator;
import java.util.ArrayList;
import java.util.List;
import org.telegram.messenger.support.widget.RecyclerView.OnItemTouchListener;
import org.telegram.messenger.support.widget.RecyclerView.ViewHolder;
import android.view.animation.Interpolator;
/**
* This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView.
* <p>
@ -156,6 +157,11 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
private static final int ACTION_MODE_DRAG_MASK = ACTION_MODE_SWIPE_MASK << DIRECTION_FLAG_COUNT;
/**
* The unit we are using to track velocity
*/
private static final int PIXELS_PER_SECOND = 1000;
/**
* Views, whose state should be cleared after they are detached from RecyclerView.
* This is necessary after swipe dismissing an item. We wait until animator finishes its job
@ -181,6 +187,16 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
float mInitialTouchY;
/**
* Set when ItemTouchHelper is assigned to a RecyclerView.
*/
float mSwipeEscapeVelocity;
/**
* Set when ItemTouchHelper is assigned to a RecyclerView.
*/
float mMaxSwipeVelocity;
/**
* The diff between the last event and initial touch.
*/
@ -367,11 +383,11 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (mVelocityTracker != null) {
mVelocityTracker
.computeCurrentVelocity(1000, mRecyclerView.getMaxFlingVelocity());
mVelocityTracker.clear();
}
// fall through
case MotionEvent.ACTION_UP:
select(null, ACTION_STATE_IDLE);
mActivePointerId = ACTIVE_POINTER_ID_NONE;
break;
@ -379,11 +395,6 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
final int pointerIndex = MotionEventCompat.getActionIndex(event);
final int pointerId = MotionEventCompat.getPointerId(event, pointerIndex);
if (pointerId == mActivePointerId) {
if (mVelocityTracker != null) {
mVelocityTracker
.computeCurrentVelocity(1000,
mRecyclerView.getMaxFlingVelocity());
}
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
@ -436,12 +447,14 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
/**
* Attaches the ItemTouchHelper to the provided RecyclerView. If TouchHelper is already
* attached
* to a RecyclerView, it will first detach from the previous one.
* attached to a RecyclerView, it will first detach from the previous one. You can call this
* method with {@code null} to detach it from the current RecyclerView.
*
* @param recyclerView The RecyclerView instance to which you want to add this helper.
* @param recyclerView The RecyclerView instance to which you want to add this helper or
* {@code null} if you want to remove ItemTouchHelper from the current
* RecyclerView.
*/
public void attachToRecyclerView(RecyclerView recyclerView) {
public void attachToRecyclerView(@Nullable RecyclerView recyclerView) {
if (mRecyclerView == recyclerView) {
return; // nothing to do
}
@ -450,6 +463,9 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
}
mRecyclerView = recyclerView;
if (mRecyclerView != null) {
final Resources resources = recyclerView.getResources();
mSwipeEscapeVelocity = AndroidUtilities.dp(120);
mMaxSwipeVelocity = AndroidUtilities.dp(800);
setupCallbacks();
}
}
@ -874,7 +890,6 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
anim.cancel();
}
mRecoverAnimations.remove(i);
anim.mViewHolder.setIsRecyclable(true);
return anim.mAnimationType;
}
}
@ -1010,7 +1025,7 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
/**
* Starts dragging the provided ViewHolder. By default, ItemTouchHelper starts a drag when a
* View is long pressed. You can disable that behavior via
* View is long pressed. You can disable that behavior by overriding
* {@link ItemTouchHelper.Callback#isLongPressDragEnabled()}.
* <p>
* For this method to work:
@ -1189,11 +1204,17 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
if ((flags & (LEFT | RIGHT)) != 0) {
final int dirFlag = mDx > 0 ? RIGHT : LEFT;
if (mVelocityTracker != null && mActivePointerId > -1) {
mVelocityTracker.computeCurrentVelocity(PIXELS_PER_SECOND,
mCallback.getSwipeVelocityThreshold(mMaxSwipeVelocity));
final float xVelocity = VelocityTrackerCompat
.getXVelocity(mVelocityTracker, mActivePointerId);
final float yVelocity = VelocityTrackerCompat
.getYVelocity(mVelocityTracker, mActivePointerId);
final int velDirFlag = xVelocity > 0f ? RIGHT : LEFT;
final float absXVelocity = Math.abs(xVelocity);
if ((velDirFlag & flags) != 0 && dirFlag == velDirFlag &&
Math.abs(xVelocity) >= mRecyclerView.getMinFlingVelocity()) {
absXVelocity >= mCallback.getSwipeEscapeVelocity(mSwipeEscapeVelocity) &&
absXVelocity > Math.abs(yVelocity)) {
return velDirFlag;
}
}
@ -1212,11 +1233,17 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
if ((flags & (UP | DOWN)) != 0) {
final int dirFlag = mDy > 0 ? DOWN : UP;
if (mVelocityTracker != null && mActivePointerId > -1) {
mVelocityTracker.computeCurrentVelocity(PIXELS_PER_SECOND,
mCallback.getSwipeVelocityThreshold(mMaxSwipeVelocity));
final float xVelocity = VelocityTrackerCompat
.getXVelocity(mVelocityTracker, mActivePointerId);
final float yVelocity = VelocityTrackerCompat
.getYVelocity(mVelocityTracker, mActivePointerId);
final int velDirFlag = yVelocity > 0f ? DOWN : UP;
final float absYVelocity = Math.abs(yVelocity);
if ((velDirFlag & flags) != 0 && velDirFlag == dirFlag &&
Math.abs(yVelocity) >= mRecyclerView.getMinFlingVelocity()) {
absYVelocity >= mCallback.getSwipeEscapeVelocity(mSwipeEscapeVelocity) &&
absYVelocity > Math.abs(xVelocity)) {
return velDirFlag;
}
}
@ -1357,7 +1384,7 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
/**
* Drag scroll speed keeps accelerating until this many milliseconds before being capped.
*/
private static final long DRAG_SCROLL_ACCELERATION_LIMIT_TIME_MS = 500;
private static final long DRAG_SCROLL_ACCELERATION_LIMIT_TIME_MS = 2000;
private int mCachedMaxScrollSpeed = -1;
@ -1372,7 +1399,8 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
}
/**
* Returns the {@link ItemTouchUIUtil} that is used by the {@link Callback} class for visual
* Returns the {@link ItemTouchUIUtil} that is used by the {@link Callback} class for
* visual
* changes on Views in response to user interactions. {@link ItemTouchUIUtil} has different
* implementations for different platform versions.
* <p>
@ -1661,6 +1689,54 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
return .5f;
}
/**
* Defines the minimum velocity which will be considered as a swipe action by the user.
* <p>
* You can increase this value to make it harder to swipe or decrease it to make it easier.
* Keep in mind that ItemTouchHelper also checks the perpendicular velocity and makes sure
* current direction velocity is larger then the perpendicular one. Otherwise, user's
* movement is ambiguous. You can change the threshold by overriding
* {@link #getSwipeVelocityThreshold(float)}.
* <p>
* The velocity is calculated in pixels per second.
* <p>
* The default framework value is passed as a parameter so that you can modify it with a
* multiplier.
*
* @param defaultValue The default value (in pixels per second) used by the
* ItemTouchHelper.
* @return The minimum swipe velocity. The default implementation returns the
* <code>defaultValue</code> parameter.
* @see #getSwipeVelocityThreshold(float)
* @see #getSwipeThreshold(ViewHolder)
*/
public float getSwipeEscapeVelocity(float defaultValue) {
return defaultValue;
}
/**
* Defines the maximum velocity ItemTouchHelper will ever calculate for pointer movements.
* <p>
* To consider a movement as swipe, ItemTouchHelper requires it to be larger than the
* perpendicular movement. If both directions reach to the max threshold, none of them will
* be considered as a swipe because it is usually an indication that user rather tried to
* scroll then swipe.
* <p>
* The velocity is calculated in pixels per second.
* <p>
* You can customize this behavior by changing this method. If you increase the value, it
* will be easier for the user to swipe diagonally and if you decrease the value, user will
* need to make a rather straight finger movement to trigger a swipe.
*
* @param defaultValue The default value(in pixels per second) used by the ItemTouchHelper.
* @return The velocity cap for pointer movements. The default implementation returns the
* <code>defaultValue</code> parameter.
* @see #getSwipeEscapeVelocity(float)
*/
public float getSwipeVelocityThreshold(float defaultValue) {
return defaultValue;
}
/**
* Called by ItemTouchHelper to select a drop target from the list of ViewHolders that
* are under the dragged View.
@ -1779,7 +1855,6 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
* @param actionState One of {@link ItemTouchHelper#ACTION_STATE_IDLE},
* {@link ItemTouchHelper#ACTION_STATE_SWIPE} or
* {@link ItemTouchHelper#ACTION_STATE_DRAG}.
*
* @see #clearView(RecyclerView, RecyclerView.ViewHolder)
*/
public void onSelectedChanged(ViewHolder viewHolder, int actionState) {
@ -1902,7 +1977,6 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
final RecoverAnimation anim = recoverAnimationList.get(i);
if (anim.mEnded && !anim.mIsPendingCleanup) {
recoverAnimationList.remove(i);
anim.mViewHolder.setIsRecyclable(true);
} else if (!anim.mEnded) {
hasRunningAnimation = true;
}
@ -2038,15 +2112,14 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
* the faster the list will scroll. Similarly, the larger portion of the View is out of
* bounds, the faster the RecyclerView will scroll.
*
* @param recyclerView The RecyclerView instance to which ItemTouchHelper is attached
* to.
* @param recyclerView The RecyclerView instance to which ItemTouchHelper is
* attached to.
* @param viewSize The total size of the View in scroll direction, excluding
* item decorations.
* @param viewSizeOutOfBounds The total size of the View that is out of bounds. This value
* is negative if the View is dragged towards left or top edge.
* @param totalSize The total size of RecyclerView in the scroll direction.
* @param msSinceStartScroll The time passed since View is kept out of bounds.
*
* @return The amount that RecyclerView should scroll. Keep in mind that this value will
* be passed to {@link RecyclerView#scrollBy(int, int)} method.
*/
@ -2314,6 +2387,9 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
@Override
public void onAnimationEnd(ValueAnimatorCompat animation) {
if (!mEnded) {
mViewHolder.setIsRecyclable(true);
}
mEnded = true;
}

View File

@ -21,16 +21,16 @@ import android.support.v4.view.ViewCompat;
import org.telegram.messenger.support.widget.RecyclerView;
import android.view.View;
/**
* Package private class to keep implementations. Putting them inside ItemTouchUIUtil makes them
* public API, which is not desired in this case.
*/
class ItemTouchUIUtilImpl {
static class Lollipop extends Honeycomb {
@Override
public void onDraw(Canvas c, RecyclerView recyclerView, View view,
float dX, float dY, int actionState, boolean isCurrentlyActive) {
float dX, float dY, int actionState, boolean isCurrentlyActive) {
if (isCurrentlyActive) {
Object originalElevation = view.getTag();
if (originalElevation == null) {
@ -85,14 +85,14 @@ class ItemTouchUIUtilImpl {
@Override
public void onDraw(Canvas c, RecyclerView recyclerView, View view,
float dX, float dY, int actionState, boolean isCurrentlyActive) {
float dX, float dY, int actionState, boolean isCurrentlyActive) {
ViewCompat.setTranslationX(view, dX);
ViewCompat.setTranslationY(view, dY);
}
@Override
public void onDrawOver(Canvas c, RecyclerView recyclerView,
View view, float dX, float dY, int actionState, boolean isCurrentlyActive) {
View view, float dX, float dY, int actionState, boolean isCurrentlyActive) {
}
}
@ -100,7 +100,7 @@ class ItemTouchUIUtilImpl {
static class Gingerbread implements ItemTouchUIUtil {
private void draw(Canvas c, RecyclerView parent, View view,
float dX, float dY) {
float dX, float dY) {
c.save();
c.translate(dX, dY);
parent.drawChild(c, view, 0);
@ -119,7 +119,7 @@ class ItemTouchUIUtilImpl {
@Override
public void onDraw(Canvas c, RecyclerView recyclerView, View view,
float dX, float dY, int actionState, boolean isCurrentlyActive) {
float dX, float dY, int actionState, boolean isCurrentlyActive) {
if (actionState != ItemTouchHelper.ACTION_STATE_DRAG) {
draw(c, recyclerView, view, dX, dY);
}
@ -127,8 +127,8 @@ class ItemTouchUIUtilImpl {
@Override
public void onDrawOver(Canvas c, RecyclerView recyclerView,
View view, float dX, float dY,
int actionState, boolean isCurrentlyActive) {
View view, float dX, float dY,
int actionState, boolean isCurrentlyActive) {
if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
draw(c, recyclerView, view, dX, dY);
}

View File

@ -67,8 +67,9 @@ public class Track {
samplingFrequencyIndexMap.put(8000, 0xb);
}
public Track(int id, MediaFormat format, boolean isAudio) throws Exception {
public Track(int id, MediaFormat format, boolean audio) throws Exception {
trackId = id;
isAudio = audio;
if (!isAudio) {
sampleDurations.add((long) 3015);
duration = 3015;
@ -136,7 +137,6 @@ public class Track {
} else {
sampleDurations.add((long) 1024);
duration = 1024;
isAudio = true;
volume = 1;
timeScale = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
handler = "soun";
@ -184,15 +184,18 @@ public class Track {
}
public void addSample(long offset, MediaCodec.BufferInfo bufferInfo) {
long delta = bufferInfo.presentationTimeUs - lastPresentationTimeUs;
if (delta < 0) {
return;
}
boolean isSyncFrame = !isAudio && (bufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0;
samples.add(new Sample(offset, bufferInfo.size));
if (syncSamples != null && isSyncFrame) {
syncSamples.add(samples.size());
}
long delta = bufferInfo.presentationTimeUs - lastPresentationTimeUs;
lastPresentationTimeUs = bufferInfo.presentationTimeUs;
delta = (delta * timeScale + 500000L) / 1000000L;
lastPresentationTimeUs = bufferInfo.presentationTimeUs;
if (!first) {
sampleDurations.add(sampleDurations.size() - 1, delta);
duration += delta;

File diff suppressed because it is too large Load Diff

View File

@ -443,11 +443,6 @@ public class ActionBarLayout extends FrameLayout {
public void onAnimationEnd(Object animator) {
onSlideAnimationEnd(backAnimation);
}
@Override
public void onAnimationCancel(Object animator) {
onSlideAnimationEnd(backAnimation);
}
});
animatorSet.start();
animationInProgress = true;
@ -693,11 +688,6 @@ public class ActionBarLayout extends FrameLayout {
public void onAnimationEnd(Object animation) {
onAnimationEndCheck(false);
}
@Override
public void onAnimationCancel(Object animation) {
onAnimationEndCheck(false);
}
});
currentAnimation.start();
} else {
@ -927,11 +917,6 @@ public class ActionBarLayout extends FrameLayout {
public void onAnimationEnd(Object animation) {
onAnimationEndCheck(false);
}
@Override
public void onAnimationCancel(Object animation) {
onAnimationEndCheck(false);
}
});
currentAnimation.start();
} else {

View File

@ -610,11 +610,6 @@ public class BottomSheet extends Dialog {
}
}
}
@Override
public void onAnimationCancel(Animator animation) {
onAnimationEnd(animation);
}
});
animatorSet.start();
}
@ -706,11 +701,6 @@ public class BottomSheet extends Dialog {
}
});
}
@Override
public void onAnimationCancel(Object animation) {
onAnimationEnd(animation);
}
});
animatorSetProxy.start();
}
@ -745,11 +735,6 @@ public class BottomSheet extends Dialog {
}
});
}
@Override
public void onAnimationCancel(Object animation) {
onAnimationEnd(animation);
}
});
animatorSetProxy.start();
}

View File

@ -187,11 +187,6 @@ public class DrawerLayoutContainer extends FrameLayout {
public void onAnimationEnd(Object animator) {
onDrawerAnimationEnd(true);
}
@Override
public void onAnimationCancel(Object animator) {
onDrawerAnimationEnd(true);
}
});
animatorSet.start();
currentAnimation = animatorSet;
@ -214,11 +209,6 @@ public class DrawerLayoutContainer extends FrameLayout {
public void onAnimationEnd(Object animator) {
onDrawerAnimationEnd(false);
}
@Override
public void onAnimationCancel(Object animator) {
onDrawerAnimationEnd(false);
}
});
animatorSet.start();
}

View File

@ -238,7 +238,7 @@ public class ContactsAdapter extends BaseSectionsAdapter {
}
} else if (type == 0) {
if (convertView == null) {
convertView = new UserCell(mContext, 58, 1);
convertView = new UserCell(mContext, 58, 1, false);
((UserCell) convertView).setStatusColors(0xffa8a8a8, 0xff3b84c0);
}

View File

@ -94,6 +94,7 @@ public class DialogsAdapter extends RecyclerView.Adapter {
} else if (viewType == 1) {
view = new LoadingCell(mContext);
}
view.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT));
return new Holder(view);
}

View File

@ -885,6 +885,7 @@ public class DialogsSearchAdapter extends BaseSearchAdapterRecycler {
view = new HashtagSearchCell(mContext);
break;
}
view.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT));
return new Holder(view);
}

View File

@ -419,6 +419,7 @@ public class MentionsAdapter extends BaseSearchAdapterRecycler {
boolean added = false;
if (searchResultBotContext == null || offset.length() == 0) {
searchResultBotContext = res.results;
contextMedia = res.gallery;
} else {
added = true;
searchResultBotContext.addAll(res.results);
@ -426,7 +427,6 @@ public class MentionsAdapter extends BaseSearchAdapterRecycler {
nextQueryOffset = "";
}
}
contextMedia = res.gallery;
searchResultHashtags = null;
searchResultUsernames = null;
searchResultCommands = null;

View File

@ -238,7 +238,7 @@ public class SearchAdapter extends BaseSearchAdapter {
} else {
if (view == null) {
if (useUserCell) {
view = new UserCell(mContext, 1, 1);
view = new UserCell(mContext, 1, 1, false);
if (checkedMap != null) {
((UserCell) view).setChecked(false, false);
}

View File

@ -263,11 +263,19 @@ public class BlockedUsersActivity extends BaseFragment implements NotificationCe
int type = getItemViewType(i);
if (type == 0) {
if (view == null) {
view = new UserCell(mContext, 1, 0);
view = new UserCell(mContext, 1, 0, false);
}
TLRPC.User user = MessagesController.getInstance().getUser(MessagesController.getInstance().blockedUsers.get(i));
if (user != null) {
((UserCell) view).setData(user, null, user.phone != null && user.phone.length() != 0 ? PhoneFormat.getInstance().format("+" + user.phone) : LocaleController.getString("NumberUnknown", R.string.NumberUnknown), 0);
String number;
if (user.bot) {
number = LocaleController.getString("Bot", R.string.Bot).substring(0, 1).toUpperCase() + LocaleController.getString("Bot", R.string.Bot).substring(1);
} else if (user.phone != null && user.phone.length() != 0) {
number = PhoneFormat.getInstance().format("+" + user.phone);
} else {
number = LocaleController.getString("NumberUnknown", R.string.NumberUnknown);
}
((UserCell) view).setData(user, null, number, 0);
}
} else if (type == 1) {
if (view == null) {

View File

@ -413,10 +413,9 @@ public class CacheControlActivity extends BaseFragment {
NativeByteBuffer data = new NativeByteBuffer(cursor2.byteArrayLength(0));
if (data != null && cursor2.byteBufferValue(0, data) != 0) {
TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false);
if (message == null) {
continue;
if (message != null) {
arrayList.add(message);
}
arrayList.add(message);
}
data.reuse();
}

View File

@ -100,12 +100,16 @@ public class BotHelpCell extends View {
MessageObject.addLinks(stringBuilder);
stringBuilder.setSpan(new TypefaceSpan(AndroidUtilities.getTypeface("fonts/rmedium.ttf")), 0, help.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
Emoji.replaceEmoji(stringBuilder, textPaint.getFontMetricsInt(), AndroidUtilities.dp(20), false);
textLayout = new StaticLayout(stringBuilder, textPaint, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
width = 0;
height = textLayout.getHeight() + AndroidUtilities.dp(4 + 18);
int count = textLayout.getLineCount();
for (int a = 0; a < count; a++) {
width = (int) Math.ceil(Math.max(width, textLayout.getLineWidth(a) + textLayout.getLineLeft(a)));
try {
textLayout = new StaticLayout(stringBuilder, textPaint, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
width = 0;
height = textLayout.getHeight() + AndroidUtilities.dp(4 + 18);
int count = textLayout.getLineCount();
for (int a = 0; a < count; a++) {
width = (int) Math.ceil(Math.max(width, textLayout.getLineWidth(a) + textLayout.getLineLeft(a)));
}
} catch (Exception e) {
FileLog.e("tmessage", e);
}
width += AndroidUtilities.dp(4 + 18);
}

View File

@ -57,6 +57,8 @@ public class ChatActionCell extends BaseCell {
private int previousWidth = 0;
private boolean imagePressed = false;
private boolean hasReplyMessage;
private MessageObject currentMessageObject;
private ChatActionCellDelegate delegate;
@ -79,10 +81,11 @@ public class ChatActionCell extends BaseCell {
}
public void setMessageObject(MessageObject messageObject) {
if (currentMessageObject == messageObject) {
if (currentMessageObject == messageObject && (hasReplyMessage || messageObject.replyMessageObject == null)) {
return;
}
currentMessageObject = messageObject;
hasReplyMessage = messageObject.replyMessageObject != null;
previousWidth = 0;
if (currentMessageObject.type == 11) {
int id = 0;
@ -220,8 +223,8 @@ public class ChatActionCell extends BaseCell {
int width = Math.max(AndroidUtilities.dp(30), MeasureSpec.getSize(widthMeasureSpec));
if (width != previousWidth) {
previousWidth = width;
textLayout = new StaticLayout(currentMessageObject.messageText, textPaint, width - AndroidUtilities.dp(30), Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false);
int maxWidth = width - AndroidUtilities.dp(30);
textLayout = new StaticLayout(currentMessageObject.messageText, textPaint, maxWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false);
textHeight = 0;
textWidth = 0;
try {
@ -230,6 +233,9 @@ public class ChatActionCell extends BaseCell {
float lineWidth;
try {
lineWidth = textLayout.getLineWidth(a);
if (lineWidth > maxWidth) {
lineWidth = maxWidth;
}
textHeight = (int)Math.max(textHeight, Math.ceil(textLayout.getLineBottom(a)));
} catch (Exception e) {
FileLog.e("tmessages", e);

View File

@ -1,5 +1,5 @@
/*
* This is the source code of Telegram for Android v. 1.3.x.
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
@ -39,7 +39,6 @@ import org.telegram.messenger.R;
import org.telegram.messenger.MessageObject;
import org.telegram.messenger.ImageReceiver;
import org.telegram.ui.Components.AvatarDrawable;
import org.telegram.ui.Components.LinkPath;
import org.telegram.ui.Components.ResourceLoader;
import org.telegram.ui.Components.StaticLayoutEx;
import org.telegram.ui.Components.TypefaceSpan;
@ -52,25 +51,22 @@ public class ChatBaseCell extends BaseCell implements MediaController.FileDownlo
void didPressedChannelAvatar(ChatBaseCell cell, TLRPC.Chat chat, int postId);
void didPressedCancelSendButton(ChatBaseCell cell);
void didLongPressed(ChatBaseCell cell);
void didPressReplyMessage(ChatBaseCell cell, int id);
void didPressUrl(MessageObject messageObject, ClickableSpan url, boolean longPress);
void didPressedReplyMessage(ChatBaseCell cell, int id);
void didPressedUrl(MessageObject messageObject, ClickableSpan url, boolean longPress);
void needOpenWebView(String url, String title, String originalUrl, int w, int h);
void didClickedImage(ChatBaseCell cell);
void didPressShare(ChatBaseCell cell);
void didPressedImage(ChatBaseCell cell);
void didPressedShare(ChatBaseCell cell);
void didPressedOther(ChatBaseCell cell);
boolean canPerformActions();
}
protected ClickableSpan pressedLink;
protected boolean linkPreviewPressed;
protected LinkPath urlPath = new LinkPath();
protected static Paint urlPaint;
private int TAG;
public boolean isChat;
protected boolean isPressed;
protected boolean forwardName;
protected boolean isHighlighted;
protected boolean media;
protected boolean mediaBackground;
protected boolean isCheckPressed = true;
private boolean wasLayout;
protected boolean isAvatarVisible;
@ -180,9 +176,6 @@ public class ChatBaseCell extends BaseCell implements MediaController.FileDownlo
replyTextPaint.linkColor = 0xff316f9f;
replyLinePaint = new Paint();
urlPaint = new Paint();
urlPaint.setColor(0x33316f9f);
}
avatarImage = new ImageReceiver(this);
avatarImage.setRoundRadius(AndroidUtilities.dp(21));
@ -211,14 +204,6 @@ public class ChatBaseCell extends BaseCell implements MediaController.FileDownlo
invalidate();
}
protected void resetPressedLink() {
if (pressedLink != null) {
pressedLink = null;
}
linkPreviewPressed = false;
invalidate();
}
public void setDelegate(ChatBaseCellDelegate delegate) {
this.delegate = delegate;
}
@ -487,7 +472,7 @@ public class ChatBaseCell extends BaseCell implements MediaController.FileDownlo
}
}
try {
nameLayout = new StaticLayout(nameStringFinal, namePaint, nameWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
nameLayout = new StaticLayout(nameStringFinal, namePaint, nameWidth + AndroidUtilities.dp(2), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
if (nameLayout != null && nameLayout.getLineCount() > 0) {
nameWidth = (int)Math.ceil(nameLayout.getLineWidth(0));
namesOffset += AndroidUtilities.dp(19);
@ -557,7 +542,7 @@ public class ChatBaseCell extends BaseCell implements MediaController.FileDownlo
namesOffset += AndroidUtilities.dp(42);
if (messageObject.contentType == 2 || messageObject.contentType == 3) {
namesOffset += AndroidUtilities.dp(4);
} else if (messageObject.contentType == 1) {
} else if (messageObject.type != 0) {
if (messageObject.type == 13) {
namesOffset -= AndroidUtilities.dp(42);
} else {
@ -590,7 +575,7 @@ public class ChatBaseCell extends BaseCell implements MediaController.FileDownlo
} else {
maxWidth = getMaxNameWidth() - AndroidUtilities.dp(22);
}
if (!media && messageObject.contentType != 0) {
if (!mediaBackground) {
maxWidth -= AndroidUtilities.dp(8);
}
@ -772,7 +757,7 @@ public class ChatBaseCell extends BaseCell implements MediaController.FileDownlo
replyPressed = false;
playSoundEffect(SoundEffectConstants.CLICK);
if (delegate != null) {
delegate.didPressReplyMessage(this, currentMessageObject.messageOwner.reply_to_msg_id);
delegate.didPressedReplyMessage(this, currentMessageObject.messageOwner.reply_to_msg_id);
}
} else if (event.getAction() == MotionEvent.ACTION_CANCEL) {
replyPressed = false;
@ -786,7 +771,7 @@ public class ChatBaseCell extends BaseCell implements MediaController.FileDownlo
sharePressed = false;
playSoundEffect(SoundEffectConstants.CLICK);
if (delegate != null) {
delegate.didPressShare(this);
delegate.didPressedShare(this);
}
} else if (event.getAction() == MotionEvent.ACTION_CANCEL) {
sharePressed = false;
@ -812,9 +797,11 @@ public class ChatBaseCell extends BaseCell implements MediaController.FileDownlo
if (changed || !wasLayout) {
layoutWidth = getMeasuredWidth();
layoutHeight = getMeasuredHeight();
if (timeTextWidth < 0) {
timeTextWidth = AndroidUtilities.dp(10);
}
timeLayout = new StaticLayout(currentTimeString, timePaint, timeTextWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
if (!media) {
if (!mediaBackground) {
if (!currentMessageObject.isOutOwner()) {
timeX = backgroundWidth - AndroidUtilities.dp(9) - timeWidth + (isChat && currentMessageObject.isFromUser() ? AndroidUtilities.dp(52) : 0);
} else {
@ -876,7 +863,7 @@ public class ChatBaseCell extends BaseCell implements MediaController.FileDownlo
avatarImage.draw(canvas);
}
if (media) {
if (mediaBackground) {
timePaint.setColor(0xffffffff);
} else {
if (currentMessageObject.isOutOwner()) {
@ -889,37 +876,37 @@ public class ChatBaseCell extends BaseCell implements MediaController.FileDownlo
Drawable currentBackgroundDrawable;
if (currentMessageObject.isOutOwner()) {
if (isDrawSelectedBackground()) {
if (!media) {
if (!mediaBackground) {
currentBackgroundDrawable = ResourceLoader.backgroundDrawableOutSelected;
} else {
currentBackgroundDrawable = ResourceLoader.backgroundMediaDrawableOutSelected;
}
} else {
if (!media) {
if (!mediaBackground) {
currentBackgroundDrawable = ResourceLoader.backgroundDrawableOut;
} else {
currentBackgroundDrawable = ResourceLoader.backgroundMediaDrawableOut;
}
}
setDrawableBounds(currentBackgroundDrawable, layoutWidth - backgroundWidth - (!media ? 0 : AndroidUtilities.dp(9)), AndroidUtilities.dp(1), backgroundWidth, layoutHeight - AndroidUtilities.dp(2));
setDrawableBounds(currentBackgroundDrawable, layoutWidth - backgroundWidth - (!mediaBackground ? 0 : AndroidUtilities.dp(9)), AndroidUtilities.dp(1), backgroundWidth, layoutHeight - AndroidUtilities.dp(2));
} else {
if (isDrawSelectedBackground()) {
if (!media) {
if (!mediaBackground) {
currentBackgroundDrawable = ResourceLoader.backgroundDrawableInSelected;
} else {
currentBackgroundDrawable = ResourceLoader.backgroundMediaDrawableInSelected;
}
} else {
if (!media) {
if (!mediaBackground) {
currentBackgroundDrawable = ResourceLoader.backgroundDrawableIn;
} else {
currentBackgroundDrawable = ResourceLoader.backgroundMediaDrawableIn;
}
}
if (isChat && currentMessageObject.isFromUser()) {
setDrawableBounds(currentBackgroundDrawable, AndroidUtilities.dp(52 + (!media ? 0 : 9)), AndroidUtilities.dp(1), backgroundWidth, layoutHeight - AndroidUtilities.dp(2));
setDrawableBounds(currentBackgroundDrawable, AndroidUtilities.dp(52 + (!mediaBackground ? 0 : 9)), AndroidUtilities.dp(1), backgroundWidth, layoutHeight - AndroidUtilities.dp(2));
} else {
setDrawableBounds(currentBackgroundDrawable, (!media ? 0 : AndroidUtilities.dp(9)), AndroidUtilities.dp(1), backgroundWidth, layoutHeight - AndroidUtilities.dp(2));
setDrawableBounds(currentBackgroundDrawable, (!mediaBackground ? 0 : AndroidUtilities.dp(9)), AndroidUtilities.dp(1), backgroundWidth, layoutHeight - AndroidUtilities.dp(2));
}
}
if (drawBackground && currentBackgroundDrawable != null) {
@ -935,7 +922,7 @@ public class ChatBaseCell extends BaseCell implements MediaController.FileDownlo
if (drawName && nameLayout != null) {
canvas.save();
if (media || currentMessageObject.isOutOwner()) {
if (mediaBackground || currentMessageObject.isOutOwner()) {
canvas.translate(nameX = currentBackgroundDrawable.getBounds().left + AndroidUtilities.dp(10) - nameOffsetX, AndroidUtilities.dp(10));
} else {
canvas.translate(nameX = currentBackgroundDrawable.getBounds().left + AndroidUtilities.dp(19) - nameOffsetX, AndroidUtilities.dp(10));
@ -962,7 +949,7 @@ public class ChatBaseCell extends BaseCell implements MediaController.FileDownlo
forwardNameX = currentBackgroundDrawable.getBounds().left + AndroidUtilities.dp(10);
} else {
forwardNamePaint.setColor(0xff006fc8);
if (media) {
if (mediaBackground) {
forwardNameX = currentBackgroundDrawable.getBounds().left + AndroidUtilities.dp(10);
} else {
forwardNameX = currentBackgroundDrawable.getBounds().left + AndroidUtilities.dp(19);
@ -972,10 +959,6 @@ public class ChatBaseCell extends BaseCell implements MediaController.FileDownlo
canvas.translate(forwardNameX - forwardNameOffsetX, forwardNameY);
forwardedNameLayout.draw(canvas);
canvas.restore();
/*if (viaWidth != 0) {
canvas.drawRect(forwardNameX + viaNameWidth, forwardNameY, forwardNameX + viaNameWidth + viaWidth, forwardNameY + AndroidUtilities.dp(32), namePaint);
}*/
}
if (currentMessageObject.isReply()) {
@ -1018,7 +1001,7 @@ public class ChatBaseCell extends BaseCell implements MediaController.FileDownlo
} else {
replyTextPaint.setColor(0xff999999);
}
if (currentMessageObject.contentType == 1 && media) {
if (mediaBackground) {
replyStartX = currentBackgroundDrawable.getBounds().left + AndroidUtilities.dp(11);
} else {
replyStartX = currentBackgroundDrawable.getBounds().left + AndroidUtilities.dp(20);
@ -1045,8 +1028,8 @@ public class ChatBaseCell extends BaseCell implements MediaController.FileDownlo
}
}
if (drawTime || !media) {
if (media) {
if (drawTime || !mediaBackground) {
if (mediaBackground) {
setDrawableBounds(ResourceLoader.mediaBackgroundDrawable, timeX - AndroidUtilities.dp(3), layoutHeight - AndroidUtilities.dp(27.5f), timeWidth + AndroidUtilities.dp(6 + (currentMessageObject.isOutOwner() ? 20 : 0)), AndroidUtilities.dp(16.5f));
ResourceLoader.mediaBackgroundDrawable.draw(canvas);
@ -1152,7 +1135,7 @@ public class ChatBaseCell extends BaseCell implements MediaController.FileDownlo
}
if (drawClock) {
if (!media) {
if (!mediaBackground) {
setDrawableBounds(ResourceLoader.clockDrawable, layoutWidth - AndroidUtilities.dp(18.5f) - ResourceLoader.clockDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(8.5f) - ResourceLoader.clockDrawable.getIntrinsicHeight());
ResourceLoader.clockDrawable.draw(canvas);
} else {
@ -1162,7 +1145,7 @@ public class ChatBaseCell extends BaseCell implements MediaController.FileDownlo
}
if (isBroadcast) {
if (drawCheck1 || drawCheck2) {
if (!media) {
if (!mediaBackground) {
setDrawableBounds(ResourceLoader.broadcastDrawable, layoutWidth - AndroidUtilities.dp(20.5f) - ResourceLoader.broadcastDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(8.0f) - ResourceLoader.broadcastDrawable.getIntrinsicHeight());
ResourceLoader.broadcastDrawable.draw(canvas);
} else {
@ -1172,7 +1155,7 @@ public class ChatBaseCell extends BaseCell implements MediaController.FileDownlo
}
} else {
if (drawCheck2) {
if (!media) {
if (!mediaBackground) {
if (drawCheck1) {
setDrawableBounds(ResourceLoader.checkDrawable, layoutWidth - AndroidUtilities.dp(22.5f) - ResourceLoader.checkDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(8.0f) - ResourceLoader.checkDrawable.getIntrinsicHeight());
} else {
@ -1189,7 +1172,7 @@ public class ChatBaseCell extends BaseCell implements MediaController.FileDownlo
}
}
if (drawCheck1) {
if (!media) {
if (!mediaBackground) {
setDrawableBounds(ResourceLoader.halfCheckDrawable, layoutWidth - AndroidUtilities.dp(18) - ResourceLoader.halfCheckDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(8.0f) - ResourceLoader.halfCheckDrawable.getIntrinsicHeight());
ResourceLoader.halfCheckDrawable.draw(canvas);
} else {
@ -1199,7 +1182,7 @@ public class ChatBaseCell extends BaseCell implements MediaController.FileDownlo
}
}
if (drawError) {
if (!media) {
if (!mediaBackground) {
setDrawableBounds(ResourceLoader.errorDrawable, layoutWidth - AndroidUtilities.dp(18) - ResourceLoader.errorDrawable.getIntrinsicWidth(), layoutHeight - AndroidUtilities.dp(6.5f) - ResourceLoader.errorDrawable.getIntrinsicHeight());
ResourceLoader.errorDrawable.draw(canvas);
} else {

View File

@ -17,6 +17,7 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.Gravity;
import android.widget.FrameLayout;
@ -66,6 +67,7 @@ public class DrawerProfileCell extends FrameLayout {
nameTextView.setMaxLines(1);
nameTextView.setSingleLine(true);
nameTextView.setGravity(Gravity.LEFT);
nameTextView.setEllipsize(TextUtils.TruncateAt.END);
addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.BOTTOM, 16, 0, 16, 28));
phoneTextView = new TextView(context);

View File

@ -16,13 +16,19 @@ import org.telegram.messenger.R;
public class ShadowSectionCell extends View {
private int size = 12;
public ShadowSectionCell(Context context) {
super(context);
setBackgroundResource(R.drawable.greydivider);
}
public void setSize(int value) {
size = value;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(12), MeasureSpec.EXACTLY));
super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(size), MeasureSpec.EXACTLY));
}
}

View File

@ -38,7 +38,7 @@ public class TextBlockCell extends FrameLayoutFixed {
textView.setTextColor(0xff212121);
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL);
addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 17, 8, 17, 8));
addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 17, 10, 17, 10));
}
public void setTextColor(int color) {

View File

@ -25,6 +25,7 @@ import org.telegram.ui.Components.Switch;
public class TextCheckCell extends FrameLayoutFixed {
private TextView textView;
private TextView valueTextView;
private Switch checkBox;
private static Paint paint;
private boolean needDivider;
@ -47,6 +48,16 @@ public class TextCheckCell extends FrameLayoutFixed {
textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL);
addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 17, 0, 17, 0));
valueTextView = new TextView(context);
valueTextView.setTextColor(0xff8a8a8a);
valueTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13);
valueTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT);
valueTextView.setLines(1);
valueTextView.setMaxLines(1);
valueTextView.setSingleLine(true);
valueTextView.setPadding(0, 0, 0, 0);
addView(valueTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 17, 35, 17, 0));
checkBox = new Switch(context);
checkBox.setDuplicateParentStateEnabled(false);
checkBox.setFocusable(false);
@ -57,7 +68,7 @@ public class TextCheckCell extends FrameLayoutFixed {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(48) + (needDivider ? 1 : 0), MeasureSpec.EXACTLY));
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(valueTextView.getVisibility() == VISIBLE ? 64 : 48) + (needDivider ? 1 : 0), MeasureSpec.EXACTLY));
}
public void setTextAndCheck(String text, boolean checked, boolean divider) {
@ -68,6 +79,28 @@ public class TextCheckCell extends FrameLayoutFixed {
}
checkBox.setChecked(checked);
needDivider = divider;
valueTextView.setVisibility(GONE);
LayoutParams layoutParams = (LayoutParams) textView.getLayoutParams();
layoutParams.height = LayoutParams.MATCH_PARENT;
layoutParams.topMargin = 0;
textView.setLayoutParams(layoutParams);
setWillNotDraw(!divider);
}
public void setTextAndValueAndCheck(String text, String value, boolean checked, boolean divider) {
textView.setText(text);
valueTextView.setText(value);
if (Build.VERSION.SDK_INT < 11) {
checkBox.resetLayout();
checkBox.requestLayout();
}
checkBox.setChecked(checked);
needDivider = divider;
valueTextView.setVisibility(VISIBLE);
LayoutParams layoutParams = (LayoutParams) textView.getLayoutParams();
layoutParams.height = LayoutParams.WRAP_CONTENT;
layoutParams.topMargin = AndroidUtilities.dp(10);
textView.setLayoutParams(layoutParams);
setWillNotDraw(!divider);
}

View File

@ -87,6 +87,10 @@ public class TextSettingsCell extends FrameLayout {
textView.setTextColor(color);
}
public void setTextValueColor(int color) {
valueTextView.setTextColor(color);
}
public void setText(String text, boolean divider) {
textView.setText(text);
valueTextView.setVisibility(INVISIBLE);

View File

@ -37,6 +37,7 @@ public class UserCell extends FrameLayout {
private ImageView imageView;
private CheckBox checkBox;
private CheckBoxSquare checkBoxBig;
private ImageView adminImage;
private AvatarDrawable avatarDrawable;
private TLObject currentObject = null;
@ -52,7 +53,7 @@ public class UserCell extends FrameLayout {
private int statusColor = 0xffa8a8a8;
private int statusOnlineColor = 0xff3b84c0;
public UserCell(Context context, int padding, int checkbox) {
public UserCell(Context context, int padding, int checkbox, boolean admin) {
super(context);
avatarDrawable = new AvatarDrawable();
@ -65,7 +66,7 @@ public class UserCell extends FrameLayout {
nameTextView.setTextColor(0xff212121);
nameTextView.setTextSize(17);
nameTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP);
addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 20, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 28 : (68 + padding), 11.5f, LocaleController.isRTL ? (68 + padding) : 28, 0));
addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 20, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 28 + (checkbox == 2 ? 18 : 0) : (68 + padding), 11.5f, LocaleController.isRTL ? (68 + padding) : 28 + (checkbox == 2 ? 18 : 0), 0));
statusTextView = new SimpleTextView(context);
statusTextView.setTextSize(14);
@ -85,6 +86,20 @@ public class UserCell extends FrameLayout {
checkBox.setVisibility(INVISIBLE);
addView(checkBox, LayoutHelper.createFrame(22, 22, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 0 : 37 + padding, 38, LocaleController.isRTL ? 37 + padding : 0, 0));
}
if (admin) {
adminImage = new ImageView(context);
adminImage.setImageResource(R.drawable.admin_star);
addView(adminImage, LayoutHelper.createFrame(16, 16, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.TOP, LocaleController.isRTL ? 24 : 0, 13.5f, LocaleController.isRTL ? 0 : 24, 0));
}
}
public void setIsAdmin(boolean value) {
if (adminImage == null) {
return;
}
adminImage.setVisibility(value ? VISIBLE : GONE);
nameTextView.setPadding(LocaleController.isRTL && value ? AndroidUtilities.dp(16) : 0, 0, !LocaleController.isRTL && value ? AndroidUtilities.dp(16) : 0, 0);
}
public void setData(TLObject user, CharSequence name, CharSequence status, int resId) {

View File

@ -166,6 +166,7 @@ public class ChangeNameActivity extends BaseFragment {
return;
}
TLRPC.TL_account_updateProfile req = new TLRPC.TL_account_updateProfile();
req.flags = 3;
currentUser.first_name = req.first_name = newFirst;
currentUser.last_name = req.last_name = newLast;
TLRPC.User user = MessagesController.getInstance().getUser(UserConfig.getClientUserId());

View File

@ -1,5 +1,5 @@
/*
* This is the source code of Telegram for Android v. 2.0.x.
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
@ -8,17 +8,25 @@
package org.telegram.ui;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Build;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.text.Editable;
import android.text.InputFilter;
import android.text.InputType;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.TypedValue;
@ -30,6 +38,7 @@ import android.view.inputmethod.EditorInfo;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
@ -59,7 +68,6 @@ import org.telegram.messenger.AnimationCompat.ViewProxy;
import org.telegram.ui.Components.HintEditText;
import org.telegram.ui.Components.LayoutHelper;
import org.telegram.ui.Components.SlideView;
import org.telegram.ui.Components.TypefaceSpan;
import java.io.BufferedReader;
import java.io.InputStreamReader;
@ -74,17 +82,21 @@ import java.util.TimerTask;
public class ChangePhoneActivity extends BaseFragment {
private int currentViewNum = 0;
private SlideView[] views = new SlideView[2];
private SlideView[] views = new SlideView[5];
private ProgressDialog progressDialog;
private Dialog permissionsDialog;
private ArrayList<String> permissionsItems = new ArrayList<>();
private boolean checkPermissions = true;
private View doneButton;
private final static int done_button = 1;
@Override
public void onFragmentDestroy() {
super.onFragmentDestroy();
for (SlideView v : views) {
if (v != null) {
v.onDestroyActivity();
for (int a = 0; a < views.length; a++) {
if (views[a] != null) {
views[a].onDestroyActivity();
}
}
if (progressDialog != null) {
@ -114,39 +126,25 @@ public class ChangePhoneActivity extends BaseFragment {
});
ActionBarMenu menu = actionBar.createMenu();
menu.addItemWithWidth(done_button, R.drawable.ic_done, AndroidUtilities.dp(56));
doneButton = menu.addItemWithWidth(done_button, R.drawable.ic_done, AndroidUtilities.dp(56));
fragmentView = new ScrollView(context);
ScrollView scrollView = (ScrollView) fragmentView;
scrollView.setFillViewport(true);
FrameLayout frameLayout = new FrameLayout(context);
scrollView.addView(frameLayout);
ScrollView.LayoutParams layoutParams = (ScrollView.LayoutParams) frameLayout.getLayoutParams();
layoutParams.width = ScrollView.LayoutParams.MATCH_PARENT;
layoutParams.height = ScrollView.LayoutParams.WRAP_CONTENT;
layoutParams.gravity = Gravity.TOP | Gravity.LEFT;
frameLayout.setLayoutParams(layoutParams);
scrollView.addView(frameLayout, LayoutHelper.createScroll(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.LEFT));
views[0] = new PhoneView(context);
views[0].setVisibility(View.VISIBLE);
frameLayout.addView(views[0], LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.LEFT, 16, 30, 16, 0));
views[1] = new LoginActivitySmsView(context, 1);
views[2] = new LoginActivitySmsView(context, 2);
views[3] = new LoginActivitySmsView(context, 3);
views[4] = new LoginActivitySmsView(context, 4);
views[1] = new LoginActivitySmsView(context);
views[1].setVisibility(View.GONE);
frameLayout.addView(views[1], LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT, 16, 30, 16, 0));
try {
if (views[0] == null || views[1] == null) {
FrameLayout parent = (FrameLayout) ((ScrollView) fragmentView).getChildAt(0);
for (int a = 0; a < views.length; a++) {
if (views[a] == null) {
views[a] = (SlideView) parent.getChildAt(a);
}
}
}
} catch (Exception e) {
FileLog.e("tmessages", e);
for (int a = 0; a < views.length; a++) {
views[a].setVisibility(a == 0 ? View.VISIBLE : View.GONE);
frameLayout.addView(views[a], LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, a == 0 ? LayoutHelper.WRAP_CONTENT : LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT, AndroidUtilities.isTablet() ? 26 : 18, 30, AndroidUtilities.isTablet() ? 26 : 18, 0));
//LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.LEFT, 16, 30, 16, 0)
}
actionBar.setTitle(views[0].getHeaderName());
@ -160,16 +158,34 @@ public class ChangePhoneActivity extends BaseFragment {
AndroidUtilities.requestAdjustResize(getParentActivity(), classGuid);
}
@Override
public void onRequestPermissionsResultFragment(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == 6) {
checkPermissions = false;
if (currentViewNum == 0) {
views[currentViewNum].onNextPressed();
}
}
}
@Override
protected void onDialogDismiss(Dialog dialog) {
if (Build.VERSION.SDK_INT >= 23 && dialog == permissionsDialog && !permissionsItems.isEmpty()) {
getParentActivity().requestPermissions(permissionsItems.toArray(new String[permissionsItems.size()]), 6);
}
}
@Override
public boolean onBackPressed() {
if (currentViewNum == 0) {
for (SlideView v : views) {
if (v != null) {
v.onDestroyActivity();
for (int a = 0; a < views.length; a++) {
if (views[a] != null) {
views[a].onDestroyActivity();
}
}
return true;
} else if (currentViewNum == 1) {
} else {
views[currentViewNum].onBackPressed();
setPage(0, true, null, true);
}
return false;
@ -217,6 +233,14 @@ public class ChangePhoneActivity extends BaseFragment {
}
public void setPage(int page, boolean animated, Bundle params, boolean back) {
if (page == 3) {
doneButton.setVisibility(View.GONE);
} else {
if (page == 0) {
checkPermissions = true;
}
doneButton.setVisibility(View.VISIBLE);
}
if(android.os.Build.VERSION.SDK_INT > 10) {
final SlideView outView = views[currentViewNum];
final SlideView newView = views[page];
@ -257,6 +281,40 @@ public class ChangePhoneActivity extends BaseFragment {
}
}
private void fillNextCodeParams(Bundle params, TLRPC.TL_auth_sentCode res) {
params.putString("phoneHash", res.phone_code_hash);
if (res.next_type instanceof TLRPC.TL_auth_codeTypeCall) {
params.putInt("nextType", 4);
} else if (res.next_type instanceof TLRPC.TL_auth_codeTypeFlashCall) {
params.putInt("nextType", 3);
} else if (res.next_type instanceof TLRPC.TL_auth_codeTypeSms) {
params.putInt("nextType", 2);
}
if (res.type instanceof TLRPC.TL_auth_sentCodeTypeApp) {
params.putInt("type", 1);
params.putInt("length", res.type.length);
setPage(1, true, params, false);
} else {
if (res.timeout == 0) {
res.timeout = 60;
}
params.putInt("timeout", res.timeout * 1000);
if (res.type instanceof TLRPC.TL_auth_sentCodeTypeCall) {
params.putInt("type", 4);
params.putInt("length", res.type.length);
setPage(4, true, params, false);
} else if (res.type instanceof TLRPC.TL_auth_sentCodeTypeFlashCall) {
params.putInt("type", 3);
params.putString("pattern", res.type.pattern);
setPage(3, true, params, false);
} else if (res.type instanceof TLRPC.TL_auth_sentCodeTypeSms) {
params.putInt("type", 2);
params.putInt("length", res.type.length);
setPage(2, true, params, false);
}
}
}
public class PhoneView extends SlideView implements AdapterView.OnItemSelectedListener {
private EditText codeField;
@ -287,9 +345,9 @@ public class ChangePhoneActivity extends BaseFragment {
countryButton.setMaxLines(1);
countryButton.setSingleLine(true);
countryButton.setEllipsize(TextUtils.TruncateAt.END);
countryButton.setGravity(Gravity.LEFT | Gravity.CENTER_HORIZONTAL);
countryButton.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_HORIZONTAL);
countryButton.setBackgroundResource(R.drawable.spinner_states);
addView(countryButton, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 36, 20, 0, 20, 14));
addView(countryButton, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 36, 0, 0, 0, 14));
countryButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
@ -315,7 +373,7 @@ public class ChangePhoneActivity extends BaseFragment {
View view = new View(context);
view.setPadding(AndroidUtilities.dp(12), 0, AndroidUtilities.dp(12), 0);
view.setBackgroundColor(0xffdbdbdb);
addView(view, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 1, 24, -17.5f, 24, 0));
addView(view, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 1, 4, -17.5f, 4, 0));
LinearLayout linearLayout = new LinearLayout(context);
linearLayout.setOrientation(HORIZONTAL);
@ -325,7 +383,7 @@ public class ChangePhoneActivity extends BaseFragment {
textView.setText("+");
textView.setTextColor(0xff212121);
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, 24, 0, 0, 0));
linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT));
codeField = new EditText(context);
codeField.setInputType(InputType.TYPE_CLASS_PHONE);
@ -354,7 +412,6 @@ public class ChangePhoneActivity extends BaseFragment {
@Override
public void afterTextChanged(Editable editable) {
if (ignoreOnTextChange) {
ignoreOnTextChange = false;
return;
}
ignoreOnTextChange = true;
@ -414,6 +471,7 @@ public class ChangePhoneActivity extends BaseFragment {
phoneField.setSelection(phoneField.length());
}
}
ignoreOnTextChange = false;
}
});
codeField.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@ -438,7 +496,7 @@ public class ChangePhoneActivity extends BaseFragment {
phoneField.setMaxLines(1);
phoneField.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
phoneField.setImeOptions(EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NO_EXTRACT_UI);
linearLayout.addView(phoneField, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 36, 0, 0, 24, 0));
linearLayout.addView(phoneField, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 36));
phoneField.addTextChangedListener(new TextWatcher() {
private int characterAction = -1;
@ -528,9 +586,9 @@ public class ChangePhoneActivity extends BaseFragment {
textView.setText(LocaleController.getString("ChangePhoneHelp", R.string.ChangePhoneHelp));
textView.setTextColor(0xff757575);
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
textView.setGravity(Gravity.LEFT);
textView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT);
textView.setLineSpacing(AndroidUtilities.dp(2), 1.0f);
addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT, 24, 28, 24, 10));
addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT, 0, 28, 0, 10));
HashMap<String, String> languageMap = new HashMap<>();
try {
@ -561,7 +619,7 @@ public class ChangePhoneActivity extends BaseFragment {
String country = null;
try {
TelephonyManager telephonyManager = (TelephonyManager)ApplicationLoader.applicationContext.getSystemService(Context.TELEPHONY_SERVICE);
TelephonyManager telephonyManager = (TelephonyManager) ApplicationLoader.applicationContext.getSystemService(Context.TELEPHONY_SERVICE);
if (telephonyManager != null) {
country = telephonyManager.getSimCountryIso().toUpperCase();
}
@ -605,6 +663,7 @@ public class ChangePhoneActivity extends BaseFragment {
String hint = phoneFormatMap.get(code);
phoneField.setHintText(hint != null ? hint.replace('X', '') : null);
countryState = 0;
ignoreOnTextChange = false;
}
}
@ -617,6 +676,7 @@ public class ChangePhoneActivity extends BaseFragment {
ignoreOnTextChange = true;
String str = countriesArray.get(i);
codeField.setText(countriesMap.get(str));
ignoreOnTextChange = false;
}
@Override
@ -626,9 +686,46 @@ public class ChangePhoneActivity extends BaseFragment {
@Override
public void onNextPressed() {
if (nextPressed) {
if (getParentActivity() == null || nextPressed) {
return;
}
TelephonyManager tm = (TelephonyManager) ApplicationLoader.applicationContext.getSystemService(Context.TELEPHONY_SERVICE);
boolean simcardAvailable = tm.getSimState() != TelephonyManager.SIM_STATE_ABSENT && tm.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE;
boolean allowCall = true;
if (Build.VERSION.SDK_INT >= 23 && simcardAvailable) {
allowCall = getParentActivity().checkSelfPermission(Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED;
boolean allowSms = getParentActivity().checkSelfPermission(Manifest.permission.RECEIVE_SMS) == PackageManager.PERMISSION_GRANTED;
if (checkPermissions) {
permissionsItems.clear();
if (!allowCall) {
permissionsItems.add(Manifest.permission.READ_PHONE_STATE);
}
if (!allowSms) {
permissionsItems.add(Manifest.permission.RECEIVE_SMS);
}
if (!permissionsItems.isEmpty()) {
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
if (preferences.getBoolean("firstlogin", true) || getParentActivity().shouldShowRequestPermissionRationale(Manifest.permission.READ_PHONE_STATE) || getParentActivity().shouldShowRequestPermissionRationale(Manifest.permission.RECEIVE_SMS)) {
preferences.edit().putBoolean("firstlogin", false).commit();
AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity());
builder.setTitle(LocaleController.getString("AppName", R.string.AppName));
builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null);
if (permissionsItems.size() == 2) {
builder.setMessage(LocaleController.getString("AllowReadCallAndSms", R.string.AllowReadCallAndSms));
} else if (!allowSms) {
builder.setMessage(LocaleController.getString("AllowReadSms", R.string.AllowReadSms));
} else {
builder.setMessage(LocaleController.getString("AllowReadCall", R.string.AllowReadCall));
}
permissionsDialog = showDialog(builder.create());
} else {
getParentActivity().requestPermissions(permissionsItems.toArray(new String[permissionsItems.size()]), 6);
}
return;
}
}
}
if (countryState == 1) {
needShowAlert(LocaleController.getString("ChooseCountry", R.string.ChooseCountry));
return;
@ -643,10 +740,20 @@ public class ChangePhoneActivity extends BaseFragment {
TLRPC.TL_account_sendChangePhoneCode req = new TLRPC.TL_account_sendChangePhoneCode();
String phone = PhoneFormat.stripExceptNumbers("" + codeField.getText() + phoneField.getText());
req.phone_number = phone;
final String phone2 = "+" + codeField.getText() + " " + phoneField.getText();
req.allow_flashcall = simcardAvailable && allowCall;
if (req.allow_flashcall) {
String number = tm.getLine1Number();
req.current_number = number != null && number.length() != 0 && (phone.contains(number) || number.contains(phone));
}
final Bundle params = new Bundle();
params.putString("phone", phone2);
params.putString("phone", "+" + codeField.getText() + phoneField.getText());
try {
params.putString("ephone", "+" + PhoneFormat.stripExceptNumbers(codeField.getText().toString()) + " " + PhoneFormat.stripExceptNumbers(phoneField.getText().toString()));
} catch (Exception e) {
FileLog.e("tmessages", e);
params.putString("ephone", "+" + phone);
}
params.putString("phoneFormated", phone);
nextPressed = true;
needShowProgress();
@ -658,10 +765,7 @@ public class ChangePhoneActivity extends BaseFragment {
public void run() {
nextPressed = false;
if (error == null) {
TLRPC.TL_account_sentChangePhoneCode res = (TLRPC.TL_account_sentChangePhoneCode)response;
params.putString("phoneHash", res.phone_code_hash);
params.putInt("calltime", res.send_call_timeout * 1000);
setPage(1, true, params, false);
fillNextCodeParams(params, (TLRPC.TL_auth_sentCode) response);
} else {
if (error.text != null) {
if (error.text.contains("PHONE_NUMBER_INVALID")) {
@ -673,7 +777,7 @@ public class ChangePhoneActivity extends BaseFragment {
} else if (error.text.startsWith("FLOOD_WAIT")) {
needShowAlert(LocaleController.getString("FloodWait", R.string.FloodWait));
} else if (error.text.startsWith("PHONE_NUMBER_OCCUPIED")) {
needShowAlert(LocaleController.formatString("ChangePhoneNumberOccupied", R.string.ChangePhoneNumberOccupied, phone2));
needShowAlert(LocaleController.formatString("ChangePhoneNumberOccupied", R.string.ChangePhoneNumberOccupied, params.getString("phone")));
} else {
needShowAlert(LocaleController.getString("ErrorOccurred", R.string.ErrorOccurred));
}
@ -709,43 +813,88 @@ public class ChangePhoneActivity extends BaseFragment {
public class LoginActivitySmsView extends SlideView implements NotificationCenter.NotificationCenterDelegate {
private class ProgressView extends View {
private Paint paint = new Paint();
private Paint paint2 = new Paint();
private float progress;
public ProgressView(Context context) {
super(context);
paint.setColor(0xffe1eaf2);
paint2.setColor(0xff62a0d0);
}
public void setProgress(float value) {
progress = value;
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
int start = (int) (getMeasuredWidth() * progress);
canvas.drawRect(0, 0, start, getMeasuredHeight(), paint2);
canvas.drawRect(start, 0, getMeasuredWidth(), getMeasuredHeight(), paint);
}
}
private String phone;
private String phoneHash;
private String requestPhone;
private String emailPhone;
private EditText codeField;
private TextView confirmTextView;
private TextView timeText;
private TextView problemText;
private Bundle currentParams;
private ProgressView progressView;
private Timer timeTimer;
private Timer codeTimer;
private int openTime;
private final Object timerSync = new Object();
private volatile int time = 60000;
private volatile int codeTime = 15000;
private double lastCurrentTime;
private double lastCodeTime;
private boolean ignoreOnTextChange;
private boolean waitingForSms = false;
private boolean nextPressed = false;
private boolean waitingForEvent;
private boolean nextPressed;
private String lastError = "";
private int currentType;
private int nextType;
private String pattern = "*";
private int length;
private int timeout;
public LoginActivitySmsView(Context context) {
public LoginActivitySmsView(Context context, final int type) {
super(context);
currentType = type;
setOrientation(VERTICAL);
confirmTextView = new TextView(context);
confirmTextView.setTextColor(0xff757575);
confirmTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
confirmTextView.setGravity(Gravity.LEFT);
confirmTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT);
confirmTextView.setLineSpacing(AndroidUtilities.dp(2), 1.0f);
addView(confirmTextView);
LayoutParams layoutParams = (LayoutParams) confirmTextView.getLayoutParams();
layoutParams.width = LayoutHelper.WRAP_CONTENT;
layoutParams.height = LayoutHelper.WRAP_CONTENT;
layoutParams.gravity = Gravity.LEFT;
layoutParams.leftMargin = AndroidUtilities.dp(24);
layoutParams.rightMargin = AndroidUtilities.dp(24);
confirmTextView.setLayoutParams(layoutParams);
if (currentType == 3) {
FrameLayout frameLayout = new FrameLayout(context);
ImageView imageView = new ImageView(context);
imageView.setImageResource(R.drawable.phone_activate);
if (LocaleController.isRTL) {
frameLayout.addView(imageView, LayoutHelper.createFrame(64, 76, Gravity.LEFT | Gravity.CENTER_VERTICAL, 2, 2, 0, 0));
frameLayout.addView(confirmTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT, 64 + 18, 0, 0, 0));
} else {
frameLayout.addView(confirmTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT, 0, 0, 64 + 18, 0));
frameLayout.addView(imageView, LayoutHelper.createFrame(64, 76, Gravity.RIGHT | Gravity.CENTER_VERTICAL, 0, 2, 0, 2));
}
addView(frameLayout, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT));
} else {
addView(confirmTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT));
}
codeField = new EditText(context);
codeField.setTextColor(0xff212121);
@ -757,15 +906,7 @@ public class ChangePhoneActivity extends BaseFragment {
codeField.setInputType(InputType.TYPE_CLASS_PHONE);
codeField.setMaxLines(1);
codeField.setPadding(0, 0, 0, 0);
addView(codeField);
layoutParams = (LayoutParams) codeField.getLayoutParams();
layoutParams.width = LayoutHelper.MATCH_PARENT;
layoutParams.height = AndroidUtilities.dp(36);
layoutParams.gravity = Gravity.CENTER_HORIZONTAL;
layoutParams.topMargin = AndroidUtilities.dp(20);
layoutParams.leftMargin = AndroidUtilities.dp(24);
layoutParams.rightMargin = AndroidUtilities.dp(24);
codeField.setLayoutParams(layoutParams);
addView(codeField, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 36, Gravity.CENTER_HORIZONTAL, 0, 20, 0, 0));
codeField.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@ -782,7 +923,7 @@ public class ChangePhoneActivity extends BaseFragment {
if (ignoreOnTextChange) {
return;
}
if (codeField.length() == 5) {
if (length != 0 && codeField.length() == length) {
onNextPressed();
}
}
@ -797,55 +938,133 @@ public class ChangePhoneActivity extends BaseFragment {
return false;
}
});
if (currentType == 3) {
codeField.setEnabled(false);
codeField.setInputType(InputType.TYPE_NULL);
codeField.setVisibility(GONE);
}
timeText = new TextView(context);
timeText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
timeText.setTextColor(0xff757575);
timeText.setLineSpacing(AndroidUtilities.dp(2), 1.0f);
timeText.setGravity(Gravity.LEFT);
addView(timeText);
layoutParams = (LayoutParams) timeText.getLayoutParams();
layoutParams.width = LayoutHelper.WRAP_CONTENT;
layoutParams.height = LayoutHelper.WRAP_CONTENT;
layoutParams.gravity = Gravity.LEFT;
layoutParams.topMargin = AndroidUtilities.dp(30);
layoutParams.leftMargin = AndroidUtilities.dp(24);
layoutParams.rightMargin = AndroidUtilities.dp(24);
timeText.setLayoutParams(layoutParams);
timeText.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT);
addView(timeText, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT, 0, 30, 0, 0));
if (currentType == 3) {
progressView = new ProgressView(context);
addView(progressView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 3, 0, 12, 0, 0));
}
problemText = new TextView(context);
problemText.setText(LocaleController.getString("DidNotGetTheCode", R.string.DidNotGetTheCode));
problemText.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT);
problemText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
problemText.setTextColor(0xff4d83b3);
problemText.setLineSpacing(AndroidUtilities.dp(2), 1.0f);
problemText.setPadding(0, AndroidUtilities.dp(2), 0, AndroidUtilities.dp(12));
addView(problemText, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT, 0, 20, 0, 0));
problemText.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (nextPressed) {
return;
}
if (nextType != 0 && nextType != 4) {
resendCode();
} else {
try {
PackageInfo pInfo = ApplicationLoader.applicationContext.getPackageManager().getPackageInfo(ApplicationLoader.applicationContext.getPackageName(), 0);
String version = String.format(Locale.US, "%s (%d)", pInfo.versionName, pInfo.versionCode);
Intent mailer = new Intent(Intent.ACTION_SEND);
mailer.setType("message/rfc822");
mailer.putExtra(Intent.EXTRA_EMAIL, new String[]{"sms@stel.com"});
mailer.putExtra(Intent.EXTRA_SUBJECT, "Android registration/login issue " + version + " " + emailPhone);
mailer.putExtra(Intent.EXTRA_TEXT, "Phone: " + requestPhone + "\nApp version: " + version + "\nOS version: SDK " + Build.VERSION.SDK_INT + "\nDevice Name: " + Build.MANUFACTURER + Build.MODEL + "\nLocale: " + Locale.getDefault() + "\nError: " + lastError);
getContext().startActivity(Intent.createChooser(mailer, "Send email..."));
} catch (Exception e) {
needShowAlert(LocaleController.getString("NoMailInstalled", R.string.NoMailInstalled));
}
}
}
});
LinearLayout linearLayout = new LinearLayout(context);
linearLayout.setGravity(Gravity.BOTTOM | Gravity.CENTER_VERTICAL);
addView(linearLayout);
layoutParams = (LayoutParams) linearLayout.getLayoutParams();
layoutParams.width = LayoutHelper.MATCH_PARENT;
layoutParams.height = LayoutHelper.MATCH_PARENT;
linearLayout.setLayoutParams(layoutParams);
linearLayout.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL);
addView(linearLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT));
TextView wrongNumber = new TextView(context);
wrongNumber.setGravity(Gravity.LEFT | Gravity.CENTER_HORIZONTAL);
wrongNumber.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_HORIZONTAL);
wrongNumber.setTextColor(0xff4d83b3);
wrongNumber.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
wrongNumber.setLineSpacing(AndroidUtilities.dp(2), 1.0f);
wrongNumber.setPadding(0, AndroidUtilities.dp(24), 0, 0);
linearLayout.addView(wrongNumber);
layoutParams = (LayoutParams) wrongNumber.getLayoutParams();
layoutParams.width = LayoutHelper.WRAP_CONTENT;
layoutParams.height = LayoutHelper.WRAP_CONTENT;
layoutParams.gravity = Gravity.BOTTOM | Gravity.LEFT;
layoutParams.bottomMargin = AndroidUtilities.dp(10);
layoutParams.leftMargin = AndroidUtilities.dp(24);
layoutParams.rightMargin = AndroidUtilities.dp(24);
wrongNumber.setLayoutParams(layoutParams);
linearLayout.addView(wrongNumber, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), 0, 0, 0, 10));
wrongNumber.setText(LocaleController.getString("WrongNumber", R.string.WrongNumber));
wrongNumber.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
TLRPC.TL_auth_cancelCode req = new TLRPC.TL_auth_cancelCode();
req.phone_number = requestPhone;
req.phone_code_hash = phoneHash;
ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
@Override
public void run(TLObject response, TLRPC.TL_error error) {
}
}, ConnectionsManager.RequestFlagFailOnServerErrors);
onBackPressed();
setPage(0, true, null, true);
}
});
}
private void resendCode() {
final Bundle params = new Bundle();
params.putString("phone", phone);
params.putString("ephone", emailPhone);
params.putString("phoneFormated", requestPhone);
nextPressed = true;
needShowProgress();
TLRPC.TL_auth_resendCode req = new TLRPC.TL_auth_resendCode();
req.phone_number = requestPhone;
req.phone_code_hash = phoneHash;
ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
@Override
public void run(final TLObject response, final TLRPC.TL_error error) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
nextPressed = false;
if (error == null) {
fillNextCodeParams(params, (TLRPC.TL_auth_sentCode) response);
} else {
if (error.text != null) {
if (error.text.contains("PHONE_NUMBER_INVALID")) {
needShowAlert(LocaleController.getString("InvalidPhoneNumber", R.string.InvalidPhoneNumber));
} else if (error.text.contains("PHONE_CODE_EMPTY") || error.text.contains("PHONE_CODE_INVALID")) {
needShowAlert(LocaleController.getString("InvalidCode", R.string.InvalidCode));
} else if (error.text.contains("PHONE_CODE_EXPIRED")) {
onBackPressed();
setPage(0, true, null, true);
needShowAlert(LocaleController.getString("CodeExpired", R.string.CodeExpired));
} else if (error.text.startsWith("FLOOD_WAIT")) {
needShowAlert(LocaleController.getString("FloodWait", R.string.FloodWait));
} else if (error.code != -1000) {
needShowAlert(LocaleController.getString("ErrorOccurred", R.string.ErrorOccurred) + "\n" + error.text);
}
}
}
needHideProgress();
}
});
}
}, ConnectionsManager.RequestFlagFailOnServerErrors);
}
@Override
public String getHeaderName() {
return LocaleController.getString("YourCode", R.string.YourCode);
@ -857,41 +1076,87 @@ public class ChangePhoneActivity extends BaseFragment {
return;
}
codeField.setText("");
AndroidUtilities.setWaitingForSms(true);
NotificationCenter.getInstance().addObserver(this, NotificationCenter.didReceiveSmsCode);
waitingForEvent = true;
if (currentType == 2) {
AndroidUtilities.setWaitingForSms(true);
NotificationCenter.getInstance().addObserver(this, NotificationCenter.didReceiveSmsCode);
} else if (currentType == 3) {
AndroidUtilities.setWaitingForCall(true);
NotificationCenter.getInstance().addObserver(this, NotificationCenter.didReceiveCall);
}
currentParams = params;
waitingForSms = true;
String phone = params.getString("phone");
phone = params.getString("phone");
emailPhone = params.getString("ephone");
requestPhone = params.getString("phoneFormated");
phoneHash = params.getString("phoneHash");
time = params.getInt("calltime");
timeout = time = params.getInt("timeout");
openTime = (int) (System.currentTimeMillis() / 1000);
nextType = params.getInt("nextType");
pattern = params.getString("pattern");
length = params.getInt("length");
if (length != 0) {
InputFilter[] inputFilters = new InputFilter[1];
inputFilters[0] = new InputFilter.LengthFilter(length);
codeField.setFilters(inputFilters);
} else {
codeField.setFilters(new InputFilter[0]);
}
if (progressView != null) {
progressView.setVisibility(nextType != 0 ? VISIBLE : GONE);
}
if (phone == null) {
return;
}
String number = PhoneFormat.getInstance().format(phone);
String str = String.format(Locale.US, LocaleController.getString("SentSmsCode", R.string.SentSmsCode) + " %s", number);
try {
SpannableStringBuilder stringBuilder = new SpannableStringBuilder(str);
TypefaceSpan span = new TypefaceSpan(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
int idx = str.indexOf(number);
stringBuilder.setSpan(span, idx, idx + number.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
confirmTextView.setText(stringBuilder);
} catch (Exception e) {
FileLog.e("tmessages", e);
confirmTextView.setText(str);
CharSequence str = "";
if (currentType == 1) {
str = AndroidUtilities.replaceTags(LocaleController.getString("SentAppCode", R.string.SentAppCode));
} else if (currentType == 2) {
str = AndroidUtilities.replaceTags(LocaleController.formatString("SentSmsCode", R.string.SentSmsCode, number));
} else if (currentType == 3) {
str = AndroidUtilities.replaceTags(LocaleController.formatString("SentCallCode", R.string.SentCallCode, number));
} else if (currentType == 4) {
str = AndroidUtilities.replaceTags(LocaleController.formatString("SentCallOnly", R.string.SentCallOnly, number));
}
confirmTextView.setText(str);
AndroidUtilities.showKeyboard(codeField);
codeField.requestFocus();
if (currentType != 3) {
AndroidUtilities.showKeyboard(codeField);
codeField.requestFocus();
} else {
AndroidUtilities.hideKeyboard(codeField);
}
destroyTimer();
destroyCodeTimer();
timeText.setText(LocaleController.formatString("CallText", R.string.CallText, 1, 0));
lastCurrentTime = System.currentTimeMillis();
createTimer();
lastCurrentTime = System.currentTimeMillis();
if (currentType == 1) {
problemText.setVisibility(VISIBLE);
timeText.setVisibility(GONE);
} else if (currentType == 3 && (nextType == 4 || nextType == 2)) {
problemText.setVisibility(GONE);
timeText.setVisibility(VISIBLE);
if (nextType == 4) {
timeText.setText(LocaleController.formatString("CallText", R.string.CallText, 1, 0));
} else if (nextType == 2) {
timeText.setText(LocaleController.formatString("SmsText", R.string.SmsText, 1, 0));
}
createTimer();
} else if (currentType == 2 && nextType == 4) {
timeText.setVisibility(VISIBLE);
timeText.setText(LocaleController.formatString("CallText", R.string.CallText, 2, 0));
problemText.setVisibility(time < 1000 ? VISIBLE : GONE);
createTimer();
} else {
timeText.setVisibility(GONE);
problemText.setVisibility(GONE);
createCodeTimer();
}
}
private void createCodeTimer() {
@ -912,6 +1177,7 @@ public class ChangePhoneActivity extends BaseFragment {
@Override
public void run() {
if (codeTime <= 1000) {
problemText.setVisibility(VISIBLE);
destroyCodeTimer();
}
}
@ -922,7 +1188,7 @@ public class ChangePhoneActivity extends BaseFragment {
private void destroyCodeTimer() {
try {
synchronized(timerSync) {
synchronized (timerSync) {
if (codeTimer != null) {
codeTimer.cancel();
codeTimer = null;
@ -941,7 +1207,10 @@ public class ChangePhoneActivity extends BaseFragment {
timeTimer.schedule(new TimerTask() {
@Override
public void run() {
double currentTime = System.currentTimeMillis();
if (timeTimer == null) {
return;
}
final double currentTime = System.currentTimeMillis();
double diff = currentTime - lastCurrentTime;
time -= diff;
lastCurrentTime = currentTime;
@ -951,27 +1220,45 @@ public class ChangePhoneActivity extends BaseFragment {
if (time >= 1000) {
int minutes = time / 1000 / 60;
int seconds = time / 1000 - minutes * 60;
timeText.setText(LocaleController.formatString("CallText", R.string.CallText, minutes, seconds));
if (nextType == 4) {
timeText.setText(LocaleController.formatString("CallText", R.string.CallText, minutes, seconds));
} else if (nextType == 2) {
timeText.setText(LocaleController.formatString("SmsText", R.string.SmsText, minutes, seconds));
}
if (progressView != null) {
progressView.setProgress(1.0f - (float) time / (float) timeout);
}
} else {
timeText.setText(LocaleController.getString("Calling", R.string.Calling));
if (progressView != null) {
progressView.setProgress(1.0f);
}
destroyTimer();
createCodeTimer();
TLRPC.TL_auth_sendCall req = new TLRPC.TL_auth_sendCall();
req.phone_number = requestPhone;
req.phone_code_hash = phoneHash;
ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
@Override
public void run(TLObject response, final TLRPC.TL_error error) {
if (error != null && error.text != null) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
lastError = error.text;
}
});
if (currentType == 3) {
AndroidUtilities.setWaitingForCall(false);
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.didReceiveCall);
waitingForEvent = false;
destroyCodeTimer();
resendCode();
} else {
timeText.setText(LocaleController.getString("Calling", R.string.Calling));
createCodeTimer();
TLRPC.TL_auth_resendCode req = new TLRPC.TL_auth_resendCode();
req.phone_number = requestPhone;
req.phone_code_hash = phoneHash;
ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
@Override
public void run(TLObject response, final TLRPC.TL_error error) {
if (error != null && error.text != null) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
lastError = error.text;
}
});
}
}
}
}, ConnectionsManager.RequestFlagFailOnServerErrors);
}, ConnectionsManager.RequestFlagFailOnServerErrors);
}
}
}
});
@ -981,7 +1268,7 @@ public class ChangePhoneActivity extends BaseFragment {
private void destroyTimer() {
try {
synchronized(timerSync) {
synchronized (timerSync) {
if (timeTimer != null) {
timeTimer.cancel();
timeTimer = null;
@ -998,9 +1285,14 @@ public class ChangePhoneActivity extends BaseFragment {
return;
}
nextPressed = true;
waitingForSms = false;
AndroidUtilities.setWaitingForSms(false);
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.didReceiveSmsCode);
if (currentType == 2) {
AndroidUtilities.setWaitingForSms(false);
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.didReceiveSmsCode);
} else if (currentType == 3) {
AndroidUtilities.setWaitingForCall(false);
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.didReceiveCall);
}
waitingForEvent = false;
final TLRPC.TL_account_changePhone req = new TLRPC.TL_account_changePhone();
req.phone_number = requestPhone;
req.phone_code = codeField.getText().toString();
@ -1028,17 +1320,29 @@ public class ChangePhoneActivity extends BaseFragment {
finishFragment();
} else {
lastError = error.text;
createTimer();
if (error.text.contains("PHONE_NUMBER_INVALID")) {
needShowAlert(LocaleController.getString("InvalidPhoneNumber", R.string.InvalidPhoneNumber));
} else if (error.text.contains("PHONE_CODE_EMPTY") || error.text.contains("PHONE_CODE_INVALID")) {
needShowAlert(LocaleController.getString("InvalidCode", R.string.InvalidCode));
} else if (error.text.contains("PHONE_CODE_EXPIRED")) {
needShowAlert(LocaleController.getString("CodeExpired", R.string.CodeExpired));
} else if (error.text.startsWith("FLOOD_WAIT")) {
needShowAlert(LocaleController.getString("FloodWait", R.string.FloodWait));
} else {
needShowAlert(error.text);
if (currentType == 3 && (nextType == 4 || nextType == 2) || currentType == 2 && nextType == 4) {
createTimer();
}
if (currentType == 2) {
AndroidUtilities.setWaitingForSms(true);
NotificationCenter.getInstance().addObserver(LoginActivitySmsView.this, NotificationCenter.didReceiveSmsCode);
} else if (currentType == 3) {
AndroidUtilities.setWaitingForCall(true);
NotificationCenter.getInstance().addObserver(LoginActivitySmsView.this, NotificationCenter.didReceiveCall);
}
waitingForEvent = true;
if (currentType != 3) {
if (error.text.contains("PHONE_NUMBER_INVALID")) {
needShowAlert(LocaleController.getString("InvalidPhoneNumber", R.string.InvalidPhoneNumber));
} else if (error.text.contains("PHONE_CODE_EMPTY") || error.text.contains("PHONE_CODE_INVALID")) {
needShowAlert(LocaleController.getString("InvalidCode", R.string.InvalidCode));
} else if (error.text.contains("PHONE_CODE_EXPIRED")) {
needShowAlert(LocaleController.getString("CodeExpired", R.string.CodeExpired));
} else if (error.text.startsWith("FLOOD_WAIT")) {
needShowAlert(LocaleController.getString("FloodWait", R.string.FloodWait));
} else {
needShowAlert(error.text);
}
}
}
}
@ -1052,19 +1356,29 @@ public class ChangePhoneActivity extends BaseFragment {
destroyTimer();
destroyCodeTimer();
currentParams = null;
AndroidUtilities.setWaitingForSms(false);
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.didReceiveSmsCode);
waitingForSms = false;
if (currentType == 2) {
AndroidUtilities.setWaitingForSms(false);
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.didReceiveSmsCode);
} else if (currentType == 3) {
AndroidUtilities.setWaitingForCall(false);
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.didReceiveCall);
}
waitingForEvent = false;
}
@Override
public void onDestroyActivity() {
super.onDestroyActivity();
AndroidUtilities.setWaitingForSms(false);
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.didReceiveSmsCode);
if (currentType == 2) {
AndroidUtilities.setWaitingForSms(false);
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.didReceiveSmsCode);
} else if (currentType == 3) {
AndroidUtilities.setWaitingForCall(false);
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.didReceiveCall);
}
waitingForEvent = false;
destroyTimer();
destroyCodeTimer();
waitingForSms = false;
}
@Override
@ -1078,15 +1392,26 @@ public class ChangePhoneActivity extends BaseFragment {
@Override
public void didReceivedNotification(int id, final Object... args) {
if (!waitingForEvent || codeField == null) {
return;
}
if (id == NotificationCenter.didReceiveSmsCode) {
if (!waitingForSms) {
return;
}
if (codeField != null) {
ignoreOnTextChange = true;
codeField.setText("" + args[0]);
onNextPressed();
ignoreOnTextChange = true;
codeField.setText("" + args[0]);
ignoreOnTextChange = false;
onNextPressed();
} else if (id == NotificationCenter.didReceiveCall) {
String num = "" + args[0];
if (!pattern.equals("*")) {
String patternNumbers = pattern.replace("*", "");
if (!num.contains(patternNumbers)) {
return;
}
}
ignoreOnTextChange = true;
codeField.setText(num);
ignoreOnTextChange = false;
onNextPressed();
}
}
}

View File

@ -39,9 +39,6 @@ import org.telegram.messenger.MessagesStorage;
import org.telegram.messenger.NotificationCenter;
import org.telegram.messenger.R;
import org.telegram.messenger.UserConfig;
import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.RequestDelegate;
import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.ActionBar;
import org.telegram.ui.ActionBar.ActionBarMenu;
@ -63,25 +60,19 @@ public class ChannelEditActivity extends BaseFragment implements AvatarUpdater.A
private View doneButton;
private EditText nameTextView;
private EditText descriptionTextView;
private EditText userNameTextView;
private BackupImageView avatarImage;
private AvatarDrawable avatarDrawable;
private AvatarUpdater avatarUpdater;
private TextView checkTextView;
private ProgressDialog progressDialog = null;
private ProgressDialog progressDialog;
private TextSettingsCell typeCell;
private TextSettingsCell adminCell;
private TLRPC.FileLocation avatar;
private int checkReqId = 0;
private String lastCheckName = null;
private Runnable checkRunnable = null;
private boolean lastNameAvailable = false;
private TLRPC.Chat currentChat;
private TLRPC.ChatFull info;
private int chatId;
private boolean allowComments = true;
private TLRPC.InputFile uploadedAvatar;
private boolean wasPrivate;
private boolean privateAlertShown;
private boolean signMessages;
private boolean createAfterUpload;
@ -131,24 +122,23 @@ public class ChannelEditActivity extends BaseFragment implements AvatarUpdater.A
}
}
}
wasPrivate = currentChat.username == null || currentChat.username.length() == 0;
avatarUpdater.parentFragment = this;
avatarUpdater.delegate = this;
allowComments = !currentChat.broadcast;
signMessages = currentChat.signatures;
NotificationCenter.getInstance().addObserver(this, NotificationCenter.chatInfoDidLoaded);
NotificationCenter.getInstance().addObserver(this, NotificationCenter.updateInterfaces);
return super.onFragmentCreate();
}
public void setInfo(TLRPC.ChatFull chatFull) {
info = chatFull;
}
@Override
public void onFragmentDestroy() {
super.onFragmentDestroy();
if (avatarUpdater != null) {
avatarUpdater.clear();
}
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.chatInfoDidLoaded);
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.updateInterfaces);
AndroidUtilities.removeAdjustResize(getParentActivity(), classGuid);
}
@ -172,7 +162,6 @@ public class ChannelEditActivity extends BaseFragment implements AvatarUpdater.A
if (donePressed) {
return;
}
donePressed = true;
if (nameTextView.length() == 0) {
Vibrator v = (Vibrator) getParentActivity().getSystemService(Context.VIBRATOR_SERVICE);
if (v != null) {
@ -181,18 +170,7 @@ public class ChannelEditActivity extends BaseFragment implements AvatarUpdater.A
AndroidUtilities.shakeView(nameTextView, 2, 0);
return;
}
if (userNameTextView != null) {
if ((currentChat.username == null && userNameTextView.length() != 0) || (currentChat.username != null && !currentChat.username.equalsIgnoreCase(userNameTextView.getText().toString()))) {
if (userNameTextView.length() != 0 && !lastNameAvailable) {
Vibrator v = (Vibrator) getParentActivity().getSystemService(Context.VIBRATOR_SERVICE);
if (v != null) {
v.vibrate(200);
}
AndroidUtilities.shakeView(checkTextView, 2, 0);
return;
}
}
}
donePressed = true;
if (avatarUpdater.uploadingAvatar != null) {
createAfterUpload = true;
@ -226,12 +204,6 @@ public class ChannelEditActivity extends BaseFragment implements AvatarUpdater.A
if (info != null && !info.about.equals(descriptionTextView.getText().toString())) {
MessagesController.getInstance().updateChannelAbout(chatId, descriptionTextView.getText().toString(), info);
}
if (userNameTextView != null) {
String oldUserName = currentChat.username != null ? currentChat.username : "";
if (!oldUserName.equals(userNameTextView.getText().toString())) {
MessagesController.getInstance().updateChannelUserName(chatId, userNameTextView.getText().toString());
}
}
if (signMessages != currentChat.signatures) {
currentChat.signatures = true;
MessagesController.getInstance().toogleChannelSignatures(chatId, signMessages);
@ -346,8 +318,9 @@ public class ChannelEditActivity extends BaseFragment implements AvatarUpdater.A
}
});
ShadowSectionCell sectionCell = new ShadowSectionCell(context);
linearLayout.addView(sectionCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
View lineView = new View(context);
lineView.setBackgroundColor(0xffcfcfcf);
linearLayout.addView(lineView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1));
linearLayout2 = new LinearLayout(context);
linearLayout2.setOrientation(LinearLayout.VERTICAL);
@ -355,7 +328,7 @@ public class ChannelEditActivity extends BaseFragment implements AvatarUpdater.A
linearLayout.addView(linearLayout2, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
descriptionTextView = new EditText(context);
descriptionTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
descriptionTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
descriptionTextView.setHintTextColor(0xff979797);
descriptionTextView.setTextColor(0xff212121);
descriptionTextView.setPadding(0, 0, 0, AndroidUtilities.dp(6));
@ -364,9 +337,9 @@ public class ChannelEditActivity extends BaseFragment implements AvatarUpdater.A
descriptionTextView.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | InputType.TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT);
descriptionTextView.setImeOptions(EditorInfo.IME_ACTION_DONE);
inputFilters = new InputFilter[1];
inputFilters[0] = new InputFilter.LengthFilter(120);
inputFilters[0] = new InputFilter.LengthFilter(255);
descriptionTextView.setFilters(inputFilters);
descriptionTextView.setHint(LocaleController.getString("DescriptionPlaceholder", R.string.DescriptionPlaceholder));
descriptionTextView.setHint(LocaleController.getString("DescriptionOptionalPlaceholder", R.string.DescriptionOptionalPlaceholder));
AndroidUtilities.clearCursorDrawable(descriptionTextView);
linearLayout2.addView(descriptionTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 17, 12, 17, 6));
descriptionTextView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@ -396,148 +369,68 @@ public class ChannelEditActivity extends BaseFragment implements AvatarUpdater.A
}
});
TextInfoPrivacyCell infoCell = new TextInfoPrivacyCell(context);
if (currentChat.megagroup) {
infoCell.setText(LocaleController.getString("DescriptionInfoMega", R.string.DescriptionInfoMega));
infoCell.setBackgroundResource(currentChat.creator ? R.drawable.greydivider : R.drawable.greydivider_bottom);
} else {
infoCell.setText(LocaleController.getString("DescriptionInfo", R.string.DescriptionInfo));
infoCell.setBackgroundResource(R.drawable.greydivider);
}
linearLayout.addView(infoCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
ShadowSectionCell sectionCell = new ShadowSectionCell(context);
sectionCell.setSize(20);
linearLayout.addView(sectionCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
if (/*BuildVars.DEBUG_VERSION && currentChat.megagroup && currentChat.creator || */!currentChat.megagroup) {
linearLayout2 = new LinearLayout(context);
linearLayout2.setOrientation(LinearLayout.VERTICAL);
linearLayout2.setBackgroundColor(0xffffffff);
linearLayout2.setPadding(0, 0, 0, AndroidUtilities.dp(7));
linearLayout.addView(linearLayout2, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
LinearLayout publicContainer = new LinearLayout(context);
publicContainer.setOrientation(LinearLayout.HORIZONTAL);
linearLayout2.addView(publicContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 36, 17, 7, 17, 0));
EditText editText = new EditText(context);
editText.setText("telegram.me/");
editText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
editText.setHintTextColor(0xff979797);
editText.setTextColor(0xff212121);
editText.setMaxLines(1);
editText.setLines(1);
editText.setEnabled(false);
editText.setBackgroundDrawable(null);
editText.setPadding(0, 0, 0, 0);
editText.setSingleLine(true);
editText.setInputType(InputType.TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT);
editText.setImeOptions(EditorInfo.IME_ACTION_DONE);
publicContainer.addView(editText, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 36));
userNameTextView = new EditText(context);
userNameTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
userNameTextView.setHintTextColor(0xff979797);
userNameTextView.setTextColor(0xff212121);
userNameTextView.setMaxLines(1);
userNameTextView.setLines(1);
userNameTextView.setBackgroundDrawable(null);
userNameTextView.setPadding(0, 0, 0, 0);
userNameTextView.setSingleLine(true);
userNameTextView.setText(currentChat.username);
userNameTextView.setInputType(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS | InputType.TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT);
userNameTextView.setImeOptions(EditorInfo.IME_ACTION_DONE);
userNameTextView.setHint(LocaleController.getString("ChannelUsernamePlaceholder", R.string.ChannelUsernamePlaceholder));
userNameTextView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (wasPrivate && hasFocus && !privateAlertShown) {
AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity());
builder.setTitle(LocaleController.getString("AppName", R.string.AppName));
if (currentChat.megagroup) {
//builder.setMessage(LocaleController.getString("MegaWasPrivateAlert", R.string.MegaWasPrivateAlert));
} else {
builder.setMessage(LocaleController.getString("ChannelWasPrivateAlert", R.string.ChannelWasPrivateAlert));
}
builder.setPositiveButton(LocaleController.getString("Close", R.string.Close), null);
showDialog(builder.create());
}
}
});
AndroidUtilities.clearCursorDrawable(userNameTextView);
publicContainer.addView(userNameTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 36));
userNameTextView.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
checkUserName(userNameTextView.getText().toString(), false);
}
@Override
public void afterTextChanged(Editable editable) {
}
});
checkTextView = new TextView(context);
checkTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
checkTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT);
checkTextView.setVisibility(View.GONE);
linearLayout2.addView(checkTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT, 17, 3, 17, 7));
infoCell = new TextInfoPrivacyCell(context);
infoCell.setBackgroundResource(R.drawable.greydivider);
if (currentChat.megagroup) {
//infoCell.setText(LocaleController.getString("MegaUsernameHelp", R.string.MegaUsernameHelp));
} else {
infoCell.setText(LocaleController.getString("ChannelUsernameHelp", R.string.ChannelUsernameHelp));
}
linearLayout.addView(infoCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
}
/*frameLayout = new FrameLayoutFixed(context);
frameLayout.setBackgroundColor(0xffffffff);
linearLayout.addView(frameLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
TextCheckCell commentsCell = new TextCheckCell(context);
commentsCell.setTextAndCheck(LocaleController.getString("Comments", R.string.Comments), allowComments, false);
commentsCell.setBackgroundResource(R.drawable.list_selector);
frameLayout.addView(commentsCell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
commentsCell.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
allowComments = !allowComments;
((TextCheckCell) v).setChecked(allowComments);
}
});
infoCell = new TextInfoPrivacyCell(context);
infoCell.setText(LocaleController.getString("CommentsInfo", R.string.CommentsInfo));
infoCell.setBackgroundResource(R.drawable.greydivider);
linearLayout.addView(infoCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));*/
if (!currentChat.megagroup && currentChat.creator) {
if (currentChat.megagroup || !currentChat.megagroup) {
frameLayout = new FrameLayoutFixed(context);
frameLayout.setBackgroundColor(0xffffffff);
linearLayout.addView(frameLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
TextCheckCell textCell = new TextCheckCell(context);
textCell.setBackgroundResource(R.drawable.list_selector);
textCell.setTextAndCheck(LocaleController.getString("ChannelSignMessages", R.string.ChannelSignMessages), signMessages, false);
frameLayout.addView(textCell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
textCell.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
signMessages = !signMessages;
((TextCheckCell) v).setChecked(signMessages);
}
});
typeCell = new TextSettingsCell(context);
updateTypeCell();
typeCell.setBackgroundResource(R.drawable.list_selector);
frameLayout.addView(typeCell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
//TODO
infoCell = new TextInfoPrivacyCell(context);
infoCell.setBackgroundResource(R.drawable.greydivider);
infoCell.setText(LocaleController.getString("ChannelSignMessagesInfo", R.string.ChannelSignMessagesInfo));
linearLayout.addView(infoCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
lineView = new View(context);
lineView.setBackgroundColor(0xffcfcfcf);
linearLayout.addView(lineView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1));
frameLayout = new FrameLayoutFixed(context);
frameLayout.setBackgroundColor(0xffffffff);
linearLayout.addView(frameLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
if (!currentChat.megagroup) {
TextCheckCell textCheckCell = new TextCheckCell(context);
textCheckCell.setBackgroundResource(R.drawable.list_selector);
textCheckCell.setTextAndCheck(LocaleController.getString("ChannelSignMessages", R.string.ChannelSignMessages), signMessages, false);
frameLayout.addView(textCheckCell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
textCheckCell.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
signMessages = !signMessages;
((TextCheckCell) v).setChecked(signMessages);
}
});
TextInfoPrivacyCell infoCell = new TextInfoPrivacyCell(context);
infoCell.setBackgroundResource(R.drawable.greydivider);
infoCell.setText(LocaleController.getString("ChannelSignMessagesInfo", R.string.ChannelSignMessagesInfo));
linearLayout.addView(infoCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
} else {
adminCell = new TextSettingsCell(context);
updateAdminCell();
adminCell.setBackgroundResource(R.drawable.list_selector);
frameLayout.addView(adminCell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
adminCell.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Bundle args = new Bundle();
args.putInt("chat_id", chatId);
args.putInt("type", 1);
presentFragment(new ChannelUsersActivity(args));
}
});
sectionCell = new ShadowSectionCell(context);
sectionCell.setSize(20);
linearLayout.addView(sectionCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
if (!currentChat.creator) {
sectionCell.setBackgroundResource(R.drawable.greydivider_bottom);
}
}
}
if (currentChat.creator) {
@ -582,7 +475,7 @@ public class ChannelEditActivity extends BaseFragment implements AvatarUpdater.A
}
});
infoCell = new TextInfoPrivacyCell(context);
TextInfoPrivacyCell infoCell = new TextInfoPrivacyCell(context);
infoCell.setBackgroundResource(R.drawable.greydivider_bottom);
if (currentChat.megagroup) {
infoCell.setText(LocaleController.getString("MegaDeleteInfo", R.string.MegaDeleteInfo));
@ -592,6 +485,27 @@ public class ChannelEditActivity extends BaseFragment implements AvatarUpdater.A
linearLayout.addView(infoCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
}
/*frameLayout = new FrameLayoutFixed(context);
frameLayout.setBackgroundColor(0xffffffff);
linearLayout.addView(frameLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
TextCheckCell commentsCell = new TextCheckCell(context);
commentsCell.setTextAndCheck(LocaleController.getString("Comments", R.string.Comments), allowComments, false);
commentsCell.setBackgroundResource(R.drawable.list_selector);
frameLayout.addView(commentsCell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
commentsCell.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
allowComments = !allowComments;
((TextCheckCell) v).setChecked(allowComments);
}
});
infoCell = new TextInfoPrivacyCell(context);
infoCell.setText(LocaleController.getString("CommentsInfo", R.string.CommentsInfo));
infoCell.setBackgroundResource(R.drawable.greydivider);
linearLayout.addView(infoCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));*/
nameTextView.setText(currentChat.title);
nameTextView.setSelection(nameTextView.length());
if (info != null) {
@ -616,6 +530,13 @@ public class ChannelEditActivity extends BaseFragment implements AvatarUpdater.A
descriptionTextView.setText(chatFull.about);
}
info = chatFull;
updateAdminCell();
updateTypeCell();
}
} else if (id == NotificationCenter.updateInterfaces) {
int updateMask = (Integer) args[0];
if ((updateMask & MessagesController.UPDATE_MASK_CHANNEL) != 0) {
updateTypeCell();
}
}
}
@ -668,153 +589,46 @@ public class ChannelEditActivity extends BaseFragment implements AvatarUpdater.A
}
}
private boolean checkUserName(final String name, boolean alert) {
if (name != null && name.length() > 0) {
checkTextView.setVisibility(View.VISIBLE);
} else {
checkTextView.setVisibility(View.GONE);
}
if (alert && name.length() == 0) {
return true;
}
if (checkRunnable != null) {
AndroidUtilities.cancelRunOnUIThread(checkRunnable);
checkRunnable = null;
lastCheckName = null;
if (checkReqId != 0) {
ConnectionsManager.getInstance().cancelRequest(checkReqId, true);
}
}
lastNameAvailable = false;
if (name != null) {
if (name.startsWith("_") || name.endsWith("_")) {
checkTextView.setText(LocaleController.getString("LinkInvalid", R.string.LinkInvalid));
checkTextView.setTextColor(0xffcf3030);
return false;
}
for (int a = 0; a < name.length(); a++) {
char ch = name.charAt(a);
if (a == 0 && ch >= '0' && ch <= '9') {
if (currentChat.megagroup) {
if (alert) {
//showErrorAlert(LocaleController.getString("LinkInvalidStartNumberMega", R.string.LinkInvalidStartNumberMega));
} else {
//checkTextView.setText(LocaleController.getString("LinkInvalidStartNumberMega", R.string.LinkInvalidStartNumberMega));
checkTextView.setTextColor(0xffcf3030);
}
} else {
if (alert) {
showErrorAlert(LocaleController.getString("LinkInvalidStartNumber", R.string.LinkInvalidStartNumber));
} else {
checkTextView.setText(LocaleController.getString("LinkInvalidStartNumber", R.string.LinkInvalidStartNumber));
checkTextView.setTextColor(0xffcf3030);
}
}
return false;
}
if (!(ch >= '0' && ch <= '9' || ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch == '_')) {
if (alert) {
showErrorAlert(LocaleController.getString("LinkInvalid", R.string.LinkInvalid));
} else {
checkTextView.setText(LocaleController.getString("LinkInvalid", R.string.LinkInvalid));
checkTextView.setTextColor(0xffcf3030);
}
return false;
}
}
}
if (name == null || name.length() < 5) {
if (currentChat.megagroup) {
if (alert) {
//showErrorAlert(LocaleController.getString("LinkInvalidShortMega", R.string.LinkInvalidShortMega));
} else {
//checkTextView.setText(LocaleController.getString("LinkInvalidShortMega", R.string.LinkInvalidShortMega));
checkTextView.setTextColor(0xffcf3030);
}
} else {
if (alert) {
showErrorAlert(LocaleController.getString("LinkInvalidShort", R.string.LinkInvalidShort));
} else {
checkTextView.setText(LocaleController.getString("LinkInvalidShort", R.string.LinkInvalidShort));
checkTextView.setTextColor(0xffcf3030);
}
}
return false;
}
if (name.length() > 32) {
if (alert) {
showErrorAlert(LocaleController.getString("LinkInvalidLong", R.string.LinkInvalidLong));
} else {
checkTextView.setText(LocaleController.getString("LinkInvalidLong", R.string.LinkInvalidLong));
checkTextView.setTextColor(0xffcf3030);
}
return false;
}
if (!alert) {
checkTextView.setText(LocaleController.getString("LinkChecking", R.string.LinkChecking));
checkTextView.setTextColor(0xff6d6d72);
lastCheckName = name;
checkRunnable = new Runnable() {
@Override
public void run() {
TLRPC.TL_channels_checkUsername req = new TLRPC.TL_channels_checkUsername();
req.username = name;
req.channel = MessagesController.getInputChannel(chatId);
checkReqId = ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
@Override
public void run(final TLObject response, final TLRPC.TL_error error) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
checkReqId = 0;
if (lastCheckName != null && lastCheckName.equals(name)) {
if (error == null && response instanceof TLRPC.TL_boolTrue) {
checkTextView.setText(LocaleController.formatString("LinkAvailable", R.string.LinkAvailable, name));
checkTextView.setTextColor(0xff26972c);
lastNameAvailable = true;
} else {
if (error != null && error.text.equals("CHANNELS_ADMIN_PUBLIC_TOO_MUCH")) {
checkTextView.setText(LocaleController.getString("ChannelPublicLimitReached", R.string.ChannelPublicLimitReached));
} else {
checkTextView.setText(LocaleController.getString("LinkInUse", R.string.LinkInUse));
}
checkTextView.setTextColor(0xffcf3030);
lastNameAvailable = false;
}
}
}
});
}
}, ConnectionsManager.RequestFlagFailOnServerErrors);
}
};
AndroidUtilities.runOnUIThread(checkRunnable, 300);
}
return true;
public void setInfo(TLRPC.ChatFull chatFull) {
info = chatFull;
}
private void showErrorAlert(String error) {
if (getParentActivity() == null) {
private void updateTypeCell() {
String type = currentChat.username == null || currentChat.username.length() == 0 ? LocaleController.getString("ChannelTypePrivate", R.string.ChannelTypePrivate) : LocaleController.getString("ChannelTypePublic", R.string.ChannelTypePublic);
if (currentChat.megagroup) {
typeCell.setTextAndValue(LocaleController.getString("GroupType", R.string.GroupType), type, false);
} else {
typeCell.setTextAndValue(LocaleController.getString("ChannelType", R.string.ChannelType), type, false);
}
if (currentChat.creator && (info == null || info.can_set_username)) {
typeCell.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Bundle args = new Bundle();
args.putInt("chat_id", chatId);
ChannelEditTypeActivity fragment = new ChannelEditTypeActivity(args);
fragment.setInfo(info);
presentFragment(fragment);
}
});
typeCell.setTextColor(0xff212121);
typeCell.setTextValueColor(0xff2f8cc9);
} else {
typeCell.setOnClickListener(null);
typeCell.setTextColor(0xffa8a8a8);
typeCell.setTextValueColor(0xffa8a8a8);
}
}
private void updateAdminCell() {
if (adminCell == null) {
return;
}
AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity());
builder.setTitle(LocaleController.getString("AppName", R.string.AppName));
switch (error) {
case "USERNAME_INVALID":
builder.setMessage(LocaleController.getString("LinkInvalid", R.string.LinkInvalid));
break;
case "USERNAME_OCCUPIED":
builder.setMessage(LocaleController.getString("LinkInUse", R.string.LinkInUse));
break;
case "USERNAMES_UNAVAILABLE":
builder.setMessage(LocaleController.getString("FeatureUnavailable", R.string.FeatureUnavailable));
break;
default:
builder.setMessage(LocaleController.getString("ErrorOccurred", R.string.ErrorOccurred));
break;
if (info != null) {
adminCell.setTextAndValue(LocaleController.getString("ChannelAdministrators", R.string.ChannelAdministrators), String.format("%d", info.admins_count), false);
} else {
adminCell.setText(LocaleController.getString("ChannelAdministrators", R.string.ChannelAdministrators), false);
}
builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null);
showDialog(builder.create());
}
}

View File

@ -0,0 +1,544 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
package org.telegram.ui;
import android.app.AlertDialog;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.os.Vibrator;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.ApplicationLoader;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.MessagesController;
import org.telegram.messenger.MessagesStorage;
import org.telegram.messenger.NotificationCenter;
import org.telegram.messenger.R;
import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.RequestDelegate;
import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.ActionBar;
import org.telegram.ui.ActionBar.ActionBarMenu;
import org.telegram.ui.ActionBar.BaseFragment;
import org.telegram.ui.Cells.HeaderCell;
import org.telegram.ui.Cells.RadioButtonCell;
import org.telegram.ui.Cells.ShadowSectionCell;
import org.telegram.ui.Cells.TextBlockCell;
import org.telegram.ui.Cells.TextInfoPrivacyCell;
import org.telegram.ui.Components.LayoutHelper;
import java.util.concurrent.Semaphore;
public class ChannelEditTypeActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate {
private LinearLayout linkContainer;
private LinearLayout publicContainer;
private TextBlockCell privateContainer;
private RadioButtonCell radioButtonCell1;
private RadioButtonCell radioButtonCell2;
private TextInfoPrivacyCell typeInfoCell;
private TextView checkTextView;
private HeaderCell headerCell;
private EditText nameTextView;
private boolean isPrivate = false;
private boolean loadingInvite;
private TLRPC.ExportedChatInvite invite;
private int checkReqId = 0;
private String lastCheckName = null;
private Runnable checkRunnable = null;
private boolean lastNameAvailable = false;
private TLRPC.Chat currentChat;
private int chatId;
private boolean donePressed;
private final static int done_button = 1;
public ChannelEditTypeActivity(Bundle args) {
super(args);
chatId = args.getInt("chat_id", 0);
}
@SuppressWarnings("unchecked")
@Override
public boolean onFragmentCreate() {
currentChat = MessagesController.getInstance().getChat(chatId);
if (currentChat == null) {
final Semaphore semaphore = new Semaphore(0);
MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() {
@Override
public void run() {
currentChat = MessagesStorage.getInstance().getChat(chatId);
semaphore.release();
}
});
try {
semaphore.acquire();
} catch (Exception e) {
FileLog.e("tmessages", e);
}
if (currentChat != null) {
MessagesController.getInstance().putChat(currentChat, true);
} else {
return false;
}
}
isPrivate = currentChat.username == null || currentChat.username.length() == 0;
NotificationCenter.getInstance().addObserver(this, NotificationCenter.chatInfoDidLoaded);
return super.onFragmentCreate();
}
@Override
public void onFragmentDestroy() {
super.onFragmentDestroy();
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.chatInfoDidLoaded);
AndroidUtilities.removeAdjustResize(getParentActivity(), classGuid);
}
@Override
public void onResume() {
super.onResume();
AndroidUtilities.requestAdjustResize(getParentActivity(), classGuid);
}
@Override
public View createView(Context context) {
actionBar.setBackButtonImage(R.drawable.ic_ab_back);
actionBar.setAllowOverlayTitle(true);
actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() {
@Override
public void onItemClick(int id) {
if (id == -1) {
finishFragment();
} else if (id == done_button) {
if (donePressed) {
return;
}
if (!isPrivate && ((currentChat.username == null && nameTextView.length() != 0) || (currentChat.username != null && !currentChat.username.equalsIgnoreCase(nameTextView.getText().toString())))) {
if (nameTextView.length() != 0 && !lastNameAvailable) {
Vibrator v = (Vibrator) getParentActivity().getSystemService(Context.VIBRATOR_SERVICE);
if (v != null) {
v.vibrate(200);
}
AndroidUtilities.shakeView(checkTextView, 2, 0);
return;
}
}
donePressed = true;
String oldUserName = currentChat.username != null ? currentChat.username : "";
String newUserName = isPrivate ? "" : nameTextView.getText().toString();
if (!oldUserName.equals(newUserName)) {
MessagesController.getInstance().updateChannelUserName(chatId, newUserName);
}
finishFragment();
}
}
});
ActionBarMenu menu = actionBar.createMenu();
menu.addItemWithWidth(done_button, R.drawable.ic_done, AndroidUtilities.dp(56));
LinearLayout linearLayout;
fragmentView = new ScrollView(context);
fragmentView.setBackgroundColor(0xfff0f0f0);
ScrollView scrollView = (ScrollView) fragmentView;
scrollView.setFillViewport(true);
linearLayout = new LinearLayout(context);
scrollView.addView(linearLayout, new ScrollView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
linearLayout.setOrientation(LinearLayout.VERTICAL);
if (currentChat.megagroup) {
actionBar.setTitle(LocaleController.getString("GroupType", R.string.GroupType));
} else {
actionBar.setTitle(LocaleController.getString("ChannelType", R.string.ChannelType));
}
LinearLayout linearLayout2 = new LinearLayout(context);
linearLayout2.setOrientation(LinearLayout.VERTICAL);
linearLayout2.setBackgroundColor(0xffffffff);
linearLayout.addView(linearLayout2, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
radioButtonCell1 = new RadioButtonCell(context);
radioButtonCell1.setBackgroundResource(R.drawable.list_selector);
if (currentChat.megagroup) {
radioButtonCell1.setTextAndValue(LocaleController.getString("MegaPublic", R.string.MegaPublic), LocaleController.getString("MegaPublicInfo", R.string.MegaPublicInfo), !isPrivate, false);
} else {
radioButtonCell1.setTextAndValue(LocaleController.getString("ChannelPublic", R.string.ChannelPublic), LocaleController.getString("ChannelPublicInfo", R.string.ChannelPublicInfo), !isPrivate, false);
}
linearLayout2.addView(radioButtonCell1, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
radioButtonCell1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!isPrivate) {
return;
}
isPrivate = false;
updatePrivatePublic();
}
});
radioButtonCell2 = new RadioButtonCell(context);
radioButtonCell2.setBackgroundResource(R.drawable.list_selector);
if (currentChat.megagroup) {
radioButtonCell2.setTextAndValue(LocaleController.getString("MegaPrivate", R.string.MegaPrivate), LocaleController.getString("MegaPrivateInfo", R.string.MegaPrivateInfo), isPrivate, false);
} else {
radioButtonCell2.setTextAndValue(LocaleController.getString("ChannelPrivate", R.string.ChannelPrivate), LocaleController.getString("ChannelPrivateInfo", R.string.ChannelPrivateInfo), isPrivate, false);
}
linearLayout2.addView(radioButtonCell2, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
radioButtonCell2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (isPrivate) {
return;
}
isPrivate = true;
updatePrivatePublic();
}
});
ShadowSectionCell sectionCell = new ShadowSectionCell(context);
linearLayout.addView(sectionCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
linkContainer = new LinearLayout(context);
linkContainer.setOrientation(LinearLayout.VERTICAL);
linkContainer.setBackgroundColor(0xffffffff);
linearLayout.addView(linkContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
headerCell = new HeaderCell(context);
linkContainer.addView(headerCell);
publicContainer = new LinearLayout(context);
publicContainer.setOrientation(LinearLayout.HORIZONTAL);
linkContainer.addView(publicContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 36, 17, 7, 17, 0));
EditText editText = new EditText(context);
editText.setText("telegram.me/");
editText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
editText.setHintTextColor(0xff979797);
editText.setTextColor(0xff212121);
editText.setMaxLines(1);
editText.setLines(1);
editText.setEnabled(false);
editText.setBackgroundDrawable(null);
editText.setPadding(0, 0, 0, 0);
editText.setSingleLine(true);
editText.setInputType(InputType.TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT);
editText.setImeOptions(EditorInfo.IME_ACTION_DONE);
publicContainer.addView(editText, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 36));
nameTextView = new EditText(context);
nameTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
if (!isPrivate) {
nameTextView.setText(currentChat.username);
}
nameTextView.setHintTextColor(0xff979797);
nameTextView.setTextColor(0xff212121);
nameTextView.setMaxLines(1);
nameTextView.setLines(1);
nameTextView.setBackgroundDrawable(null);
nameTextView.setPadding(0, 0, 0, 0);
nameTextView.setSingleLine(true);
nameTextView.setInputType(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS | InputType.TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT);
nameTextView.setImeOptions(EditorInfo.IME_ACTION_DONE);
nameTextView.setHint(LocaleController.getString("ChannelUsernamePlaceholder", R.string.ChannelUsernamePlaceholder));
AndroidUtilities.clearCursorDrawable(nameTextView);
publicContainer.addView(nameTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 36));
nameTextView.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
checkUserName(nameTextView.getText().toString(), false);
}
@Override
public void afterTextChanged(Editable editable) {
}
});
privateContainer = new TextBlockCell(context);
privateContainer.setBackgroundResource(R.drawable.list_selector);
linkContainer.addView(privateContainer);
privateContainer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (invite == null) {
return;
}
try {
if (Build.VERSION.SDK_INT < 11) {
android.text.ClipboardManager clipboard = (android.text.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE);
clipboard.setText(invite.link);
} else {
android.content.ClipboardManager clipboard = (android.content.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE);
android.content.ClipData clip = android.content.ClipData.newPlainText("label", invite.link);
clipboard.setPrimaryClip(clip);
}
Toast.makeText(getParentActivity(), LocaleController.getString("LinkCopied", R.string.LinkCopied), Toast.LENGTH_SHORT).show();
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
});
checkTextView = new TextView(context);
checkTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
checkTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT);
checkTextView.setVisibility(View.GONE);
linkContainer.addView(checkTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT, 17, 3, 17, 7));
typeInfoCell = new TextInfoPrivacyCell(context);
typeInfoCell.setBackgroundResource(R.drawable.greydivider_bottom);
linearLayout.addView(typeInfoCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
updatePrivatePublic();
return fragmentView;
}
@Override
public void didReceivedNotification(int id, Object... args) {
if (id == NotificationCenter.chatInfoDidLoaded) {
TLRPC.ChatFull chatFull = (TLRPC.ChatFull) args[0];
if (chatFull.id == chatId) {
invite = chatFull.exported_invite;
updatePrivatePublic();
}
}
}
public void setInfo(TLRPC.ChatFull chatFull) {
if (chatFull != null) {
if (chatFull.exported_invite instanceof TLRPC.TL_chatInviteExported) {
invite = chatFull.exported_invite;
} else {
generateLink();
}
}
}
private void updatePrivatePublic() {
radioButtonCell1.setChecked(!isPrivate, true);
radioButtonCell2.setChecked(isPrivate, true);
if (currentChat.megagroup) {
typeInfoCell.setText(isPrivate ? LocaleController.getString("MegaPrivateLinkHelp", R.string.MegaPrivateLinkHelp) : LocaleController.getString("MegaUsernameHelp", R.string.MegaUsernameHelp));
headerCell.setText(isPrivate ? LocaleController.getString("ChannelInviteLinkTitle", R.string.ChannelInviteLinkTitle) : LocaleController.getString("ChannelLinkTitle", R.string.ChannelLinkTitle));
} else {
typeInfoCell.setText(isPrivate ? LocaleController.getString("ChannelPrivateLinkHelp", R.string.ChannelPrivateLinkHelp) : LocaleController.getString("ChannelUsernameHelp", R.string.ChannelUsernameHelp));
headerCell.setText(isPrivate ? LocaleController.getString("ChannelInviteLinkTitle", R.string.ChannelInviteLinkTitle) : LocaleController.getString("ChannelLinkTitle", R.string.ChannelLinkTitle));
}
publicContainer.setVisibility(isPrivate ? View.GONE : View.VISIBLE);
privateContainer.setVisibility(isPrivate ? View.VISIBLE : View.GONE);
linkContainer.setPadding(0, 0, 0, isPrivate ? 0 : AndroidUtilities.dp(7));
privateContainer.setText(invite != null ? invite.link : LocaleController.getString("Loading", R.string.Loading), false);
nameTextView.clearFocus();
checkTextView.setVisibility(!isPrivate && checkTextView.length() != 0 ? View.VISIBLE : View.GONE);
AndroidUtilities.hideKeyboard(nameTextView);
}
private boolean checkUserName(final String name, boolean alert) {
if (name != null && name.length() > 0) {
checkTextView.setVisibility(View.VISIBLE);
} else {
checkTextView.setVisibility(View.GONE);
}
if (alert && name.length() == 0) {
return true;
}
if (checkRunnable != null) {
AndroidUtilities.cancelRunOnUIThread(checkRunnable);
checkRunnable = null;
lastCheckName = null;
if (checkReqId != 0) {
ConnectionsManager.getInstance().cancelRequest(checkReqId, true);
}
}
lastNameAvailable = false;
if (name != null) {
if (name.startsWith("_") || name.endsWith("_")) {
checkTextView.setText(LocaleController.getString("LinkInvalid", R.string.LinkInvalid));
checkTextView.setTextColor(0xffcf3030);
return false;
}
for (int a = 0; a < name.length(); a++) {
char ch = name.charAt(a);
if (a == 0 && ch >= '0' && ch <= '9') {
if (currentChat.megagroup) {
if (alert) {
showErrorAlert(LocaleController.getString("LinkInvalidStartNumberMega", R.string.LinkInvalidStartNumberMega));
} else {
checkTextView.setText(LocaleController.getString("LinkInvalidStartNumberMega", R.string.LinkInvalidStartNumberMega));
checkTextView.setTextColor(0xffcf3030);
}
} else {
if (alert) {
showErrorAlert(LocaleController.getString("LinkInvalidStartNumber", R.string.LinkInvalidStartNumber));
} else {
checkTextView.setText(LocaleController.getString("LinkInvalidStartNumber", R.string.LinkInvalidStartNumber));
checkTextView.setTextColor(0xffcf3030);
}
}
return false;
}
if (!(ch >= '0' && ch <= '9' || ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch == '_')) {
if (alert) {
showErrorAlert(LocaleController.getString("LinkInvalid", R.string.LinkInvalid));
} else {
checkTextView.setText(LocaleController.getString("LinkInvalid", R.string.LinkInvalid));
checkTextView.setTextColor(0xffcf3030);
}
return false;
}
}
}
if (name == null || name.length() < 5) {
if (currentChat.megagroup) {
if (alert) {
showErrorAlert(LocaleController.getString("LinkInvalidShortMega", R.string.LinkInvalidShortMega));
} else {
checkTextView.setText(LocaleController.getString("LinkInvalidShortMega", R.string.LinkInvalidShortMega));
checkTextView.setTextColor(0xffcf3030);
}
} else {
if (alert) {
showErrorAlert(LocaleController.getString("LinkInvalidShort", R.string.LinkInvalidShort));
} else {
checkTextView.setText(LocaleController.getString("LinkInvalidShort", R.string.LinkInvalidShort));
checkTextView.setTextColor(0xffcf3030);
}
}
return false;
}
if (name.length() > 32) {
if (alert) {
showErrorAlert(LocaleController.getString("LinkInvalidLong", R.string.LinkInvalidLong));
} else {
checkTextView.setText(LocaleController.getString("LinkInvalidLong", R.string.LinkInvalidLong));
checkTextView.setTextColor(0xffcf3030);
}
return false;
}
if (!alert) {
checkTextView.setText(LocaleController.getString("LinkChecking", R.string.LinkChecking));
checkTextView.setTextColor(0xff6d6d72);
lastCheckName = name;
checkRunnable = new Runnable() {
@Override
public void run() {
TLRPC.TL_channels_checkUsername req = new TLRPC.TL_channels_checkUsername();
req.username = name;
req.channel = MessagesController.getInputChannel(chatId);
checkReqId = ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
@Override
public void run(final TLObject response, final TLRPC.TL_error error) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
checkReqId = 0;
if (lastCheckName != null && lastCheckName.equals(name)) {
if (error == null && response instanceof TLRPC.TL_boolTrue) {
checkTextView.setText(LocaleController.formatString("LinkAvailable", R.string.LinkAvailable, name));
checkTextView.setTextColor(0xff26972c);
lastNameAvailable = true;
} else {
if (error != null && error.text.equals("CHANNELS_ADMIN_PUBLIC_TOO_MUCH")) {
checkTextView.setText(LocaleController.getString("ChangePublicLimitReached", R.string.ChangePublicLimitReached));
} else {
checkTextView.setText(LocaleController.getString("LinkInUse", R.string.LinkInUse));
}
checkTextView.setTextColor(0xffcf3030);
lastNameAvailable = false;
}
}
}
});
}
}, ConnectionsManager.RequestFlagFailOnServerErrors);
}
};
AndroidUtilities.runOnUIThread(checkRunnable, 300);
}
return true;
}
private void generateLink() {
if (loadingInvite || invite != null) {
return;
}
loadingInvite = true;
TLRPC.TL_channels_exportInvite req = new TLRPC.TL_channels_exportInvite();
req.channel = MessagesController.getInputChannel(chatId);
ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
@Override
public void run(final TLObject response, final TLRPC.TL_error error) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
if (error == null) {
invite = (TLRPC.ExportedChatInvite) response;
}
loadingInvite = false;
privateContainer.setText(invite != null ? invite.link : LocaleController.getString("Loading", R.string.Loading), false);
}
});
}
});
}
private void showErrorAlert(String error) {
if (getParentActivity() == null) {
return;
}
AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity());
builder.setTitle(LocaleController.getString("AppName", R.string.AppName));
switch (error) {
case "USERNAME_INVALID":
builder.setMessage(LocaleController.getString("LinkInvalid", R.string.LinkInvalid));
break;
case "USERNAME_OCCUPIED":
builder.setMessage(LocaleController.getString("LinkInUse", R.string.LinkInUse));
break;
case "USERNAMES_UNAVAILABLE":
builder.setMessage(LocaleController.getString("FeatureUnavailable", R.string.FeatureUnavailable));
break;
default:
builder.setMessage(LocaleController.getString("ErrorOccurred", R.string.ErrorOccurred));
break;
}
builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null);
showDialog(builder.create());
}
}

View File

@ -560,7 +560,7 @@ public class ChannelUsersActivity extends BaseFragment implements NotificationCe
int viewType = getItemViewType(i);
if (viewType == 0) {
if (view == null) {
view = new UserCell(mContext, 1, 0);
view = new UserCell(mContext, 1, 0, false);
view.setBackgroundColor(0xffffffff);
}
UserCell userCell = (UserCell) view;

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,7 @@ import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.LocaleController;
@ -22,10 +23,14 @@ import org.telegram.messenger.MessagesStorage;
import org.telegram.messenger.NotificationsController;
import org.telegram.messenger.ApplicationLoader;
import org.telegram.messenger.R;
import org.telegram.messenger.Utilities;
import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.RequestDelegate;
import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.BaseFragment;
import org.telegram.ui.ActionBar.BottomSheet;
import org.telegram.ui.ReportOtherActivity;
public class AlertsCreator {
@ -82,6 +87,68 @@ public class AlertsCreator {
return builder.create();
}
public static Dialog createReportAlert(Context context, final long dialog_id, final BaseFragment parentFragment) {
if (context == null || parentFragment == null) {
return null;
}
BottomSheet.Builder builder = new BottomSheet.Builder(context);
builder.setTitle(LocaleController.getString("ReportChat", R.string.ReportChat));
CharSequence[] items = new CharSequence[]{
LocaleController.getString("ReportChatSpam", R.string.ReportChatSpam),
LocaleController.getString("ReportChatViolence", R.string.ReportChatViolence),
LocaleController.getString("ReportChatPornography", R.string.ReportChatPornography),
LocaleController.getString("ReportChatOther", R.string.ReportChatOther)
};
builder.setItems(items, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
if (i == 3) {
Bundle args = new Bundle();
args.putLong("dialog_id", dialog_id);
parentFragment.presentFragment(new ReportOtherActivity(args));
return;
}
TLRPC.TL_account_reportPeer req = new TLRPC.TL_account_reportPeer();
req.peer = MessagesController.getInputPeer((int) dialog_id);
if (i == 0) {
req.reason = new TLRPC.TL_inputReportReasonSpam();
} else if (i == 1) {
req.reason = new TLRPC.TL_inputReportReasonViolence();
} else if (i == 2) {
req.reason = new TLRPC.TL_inputReportReasonPornography();
}
ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
@Override
public void run(TLObject response, TLRPC.TL_error error) {
}
});
}
}
);
return builder.create();
}
public static void showFloodWaitAlert(String error, final BaseFragment fragment) {
if (error == null || !error.startsWith("FLOOD_WAIT") || fragment == null || fragment.getParentActivity() == null) {
return;
}
int time = Utilities.parseInt(error);
String timeString;
if (time < 60) {
timeString = LocaleController.formatPluralString("Seconds", time);
} else {
timeString = LocaleController.formatPluralString("Minutes", time / 60);
}
AlertDialog.Builder builder = new AlertDialog.Builder(fragment.getParentActivity());
builder.setTitle(LocaleController.getString("AppName", R.string.AppName));
builder.setMessage(LocaleController.formatString("FloodWaitTime", R.string.FloodWaitTime, timeString));
builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null);
fragment.showDialog(builder.create(), true);
}
public static void showAddUserAlert(String error, final BaseFragment fragment, boolean isChannel) {
if (error == null || fragment == null || fragment.getParentActivity() == null) {
return;

View File

@ -11,7 +11,9 @@ package org.telegram.ui.Components;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.Canvas;
@ -482,8 +484,13 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
emojiButton = new ImageView(context);
emojiButton.setImageResource(R.drawable.ic_msg_panel_smiles);
emojiButton.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
emojiButton.setPadding(AndroidUtilities.dp(4), AndroidUtilities.dp(1), 0, 0);
frameLayout.addView(emojiButton, LayoutHelper.createFrame(48, 48, Gravity.BOTTOM));
emojiButton.setPadding(0, AndroidUtilities.dp(1), 0, 0);
if (Build.VERSION.SDK_INT >= 21) {
emojiButton.setBackgroundResource(R.drawable.circle_selector);
frameLayout.addView(emojiButton, LayoutHelper.createFrame(44, 44, Gravity.BOTTOM | Gravity.LEFT, 4, 0, 0, 2));
} else {
frameLayout.addView(emojiButton, LayoutHelper.createFrame(48, 48, Gravity.BOTTOM | Gravity.LEFT, 3, 0, 0, 0));
}
emojiButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
@ -583,7 +590,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
if (innerTextChange != 2 && before != count && (count - before) > 1) {
processChange = true;
}
if (!isAsAdmin && message.length() != 0 && lastTypingTimeSend < System.currentTimeMillis() - 5000 && !ignoreTextChange) {
if (editingMessageObject == null && !isAsAdmin && message.length() != 0 && lastTypingTimeSend < System.currentTimeMillis() - 5000 && !ignoreTextChange) {
int currentTime = ConnectionsManager.getInstance().getCurrentTime();
TLRPC.User currentUser = null;
if ((int) dialog_id > 0) {
@ -633,7 +640,12 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
botButton.setImageResource(R.drawable.bot_keyboard2);
botButton.setScaleType(ImageView.ScaleType.CENTER);
botButton.setVisibility(GONE);
attachButton.addView(botButton, LayoutHelper.createLinear(48, 48));
if (Build.VERSION.SDK_INT >= 21) {
botButton.setBackgroundResource(R.drawable.circle_selector);
attachButton.addView(botButton, LayoutHelper.createLinear(44, 44, Gravity.CENTER_VERTICAL, 2, 0, 2, 0));
} else {
attachButton.addView(botButton, LayoutHelper.createLinear(48, 48));
}
botButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
@ -660,7 +672,12 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
asAdminButton.setImageResource(isAsAdmin ? R.drawable.publish_active : R.drawable.publish);
asAdminButton.setScaleType(ImageView.ScaleType.CENTER);
asAdminButton.setVisibility(adminModeAvailable ? VISIBLE : GONE);
attachButton.addView(asAdminButton, LayoutHelper.createLinear(48, 48));
if (Build.VERSION.SDK_INT >= 21) {
asAdminButton.setBackgroundResource(R.drawable.circle_selector);
attachButton.addView(asAdminButton, LayoutHelper.createLinear(44, 44, Gravity.CENTER_VERTICAL, 2, 0, 2, 0));
} else {
attachButton.addView(asAdminButton, LayoutHelper.createLinear(48, 48));
}
asAdminButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
@ -676,7 +693,12 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
notifyButton.setImageResource(silent ? R.drawable.notify_members_off : R.drawable.notify_members_on);
notifyButton.setScaleType(ImageView.ScaleType.CENTER);
notifyButton.setVisibility(canWriteToChannel ? VISIBLE : GONE);
attachButton.addView(notifyButton, LayoutHelper.createLinear(48, 48));
if (Build.VERSION.SDK_INT >= 21) {
notifyButton.setBackgroundResource(R.drawable.circle_selector);
attachButton.addView(notifyButton, LayoutHelper.createLinear(44, 44, Gravity.CENTER_VERTICAL, 2, 0, 2, 0));
} else {
attachButton.addView(notifyButton, LayoutHelper.createLinear(48, 48));
}
notifyButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
@ -689,6 +711,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
} else {
Toast.makeText(parentActivity, LocaleController.getString("ChannelNotifyMembersInfoOn", R.string.ChannelNotifyMembersInfoOn), Toast.LENGTH_SHORT).show();
}
updateFieldHint();
}
});
}
@ -802,11 +825,9 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
public boolean onTouch(View view, MotionEvent motionEvent) {
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
if (parentFragment != null) {
if (Build.VERSION.SDK_INT >= 23) {
if (parentActivity.checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
parentActivity.requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, 3);
return false;
}
if (Build.VERSION.SDK_INT >= 23 && parentActivity.checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
parentActivity.requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, 3);
return false;
}
String action;
@ -1171,7 +1192,15 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
if (editingMessageObject != null) {
messageEditText.setHint(editingCaption ? LocaleController.getString("Caption", R.string.Caption) : LocaleController.getString("TypeMessage", R.string.TypeMessage));
} else {
messageEditText.setHint(isAsAdmin ? LocaleController.getString("ChannelBroadcast", R.string.ChannelBroadcast) : LocaleController.getString("ChannelComment", R.string.ChannelComment));
if (isAsAdmin) {
if (silent) {
messageEditText.setHint(LocaleController.getString("ChannelSilentBroadcast", R.string.ChannelSilentBroadcast));
} else {
messageEditText.setHint(LocaleController.getString("ChannelBroadcast", R.string.ChannelBroadcast));
}
} else {
messageEditText.setHint(LocaleController.getString("ChannelComment", R.string.ChannelComment));
}
}
} else {
messageEditText.setHint(LocaleController.getString("TypeMessage", R.string.TypeMessage));
@ -1764,7 +1793,12 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
ViewGroup viewGroup = (ViewGroup) view.getParent();
viewGroup.removeView(view);
}
attachButton.addView(view, LayoutHelper.createLinear(48, 48));
if (Build.VERSION.SDK_INT >= 21) {
view.setBackgroundResource(R.drawable.circle_selector);
attachButton.addView(view, LayoutHelper.createLinear(44, 44, Gravity.CENTER_VERTICAL, 2, 0, 2, 0));
} else {
attachButton.addView(view, LayoutHelper.createLinear(48, 48));
}
}
private void updateBotButton() {
@ -1920,7 +1954,10 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
@Override
public void onGifSelected(TLRPC.Document gif) {
SendMessagesHelper.getInstance().sendMessage((TLRPC.TL_document) gif, null, null, dialog_id, replyingMessageObject, asAdmin(), null);
SendMessagesHelper.getInstance().sendSticker(gif, dialog_id, replyingMessageObject, asAdmin());
if ((int) dialog_id == 0) {
MessagesController.getInstance().saveGif(gif);
}
if (delegate != null) {
delegate.onMessageSend(null);
}
@ -1944,6 +1981,24 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
public void onStickersTab(boolean opened) {
delegate.onStickersTab(opened);
}
@Override
public void onClearEmojiRecent() {
if (parentFragment == null || parentActivity == null) {
return;
}
AlertDialog.Builder builder = new AlertDialog.Builder(parentActivity);
builder.setTitle(LocaleController.getString("AppName", R.string.AppName));
builder.setMessage(LocaleController.getString("ClearRecentEmoji", R.string.ClearRecentEmoji));
builder.setPositiveButton(LocaleController.getString("ClearButton", R.string.ClearButton).toUpperCase(), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
emojiView.clearRecentEmoji();
}
});
builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null);
parentFragment.showDialog(builder.create());
}
});
emojiView.setVisibility(GONE);
sizeNotifierLayout.addView(emojiView);
@ -2066,6 +2121,10 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
return editingCaption;
}
public boolean hasAudioToSend() {
return audioToSendMessageObject != null;
}
public void openKeyboard() {
AndroidUtilities.showKeyboard(messageEditText);
}

View File

@ -48,6 +48,7 @@ import org.telegram.messenger.LocaleController;
import org.telegram.messenger.MediaController;
import org.telegram.messenger.MessagesStorage;
import org.telegram.messenger.NotificationCenter;
import org.telegram.messenger.Utilities;
import org.telegram.messenger.query.StickersQuery;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.R;
@ -77,6 +78,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
void onGifSelected(TLRPC.Document gif);
void onGifTab(boolean opened);
void onStickersTab(boolean opened);
void onClearEmojiRecent();
}
private static final Field superListenerField;
@ -160,6 +162,8 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
pickerViewPopup.showAsDropDown(view, xOffset, -view.getMeasuredHeight() - popupHeight + (view.getMeasuredHeight() - emojiSize) / 2 - yOffset);
view.getParent().requestDisallowInterceptTouchEvent(true);
return true;
} else if (pager.getCurrentItem() == 0) {
listener.onClearEmojiRecent();
}
return false;
}
@ -519,7 +523,6 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
private GifsAdapter gifsAdapter;
private AdapterView.OnItemClickListener stickersOnItemClickListener;
private EmojiColorPickerView pickerView;
private EmojiPopupWindow pickerViewPopup;
private int popupWidth;
@ -531,8 +534,6 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
private int gifTabBum = -2;
private boolean switchToGifTab;
private int oldWidth;
private int lastNotifyWidth;
@ -667,7 +668,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
return;
}
TLRPC.Document document = recentImages.get(position).document;
listener.onStickerSelected(document);
listener.onGifSelected(document);
}
});
gifsGridView.setOnItemLongClickListener(new RecyclerListView.OnItemLongClickListener() {
@ -966,6 +967,15 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
loadRecents();
}
public void clearRecentEmoji() {
SharedPreferences preferences = getContext().getSharedPreferences("emoji", Activity.MODE_PRIVATE);
preferences.edit().putBoolean("filled_default", true).commit();
emojiUseHistory.clear();
recentEmoji.clear();
saveRecentEmoji();
adapters.get(0).notifyDataSetChanged();
}
private void showGifTab() {
gifsGridView.setVisibility(VISIBLE);
stickersGridView.setVisibility(GONE);
@ -1268,7 +1278,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
String[] args = str.split(",");
for (String arg : args) {
String[] args2 = arg.split("=");
long value = Long.parseLong(args2[0]);
long value = Utilities.parseLong(args2[0]);
String string = "";
for (int a = 0; a < 4; a++) {
char ch = (char) value;
@ -1279,7 +1289,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
}
}
if (string.length() > 0) {
emojiUseHistory.put(string, Integer.parseInt(args2[1]));
emojiUseHistory.put(string, Utilities.parseInt(args2[1]));
}
}
}
@ -1291,22 +1301,25 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
String[] args = str.split(",");
for (String arg : args) {
String[] args2 = arg.split("=");
emojiUseHistory.put(args2[0], Integer.parseInt(args2[1]));
emojiUseHistory.put(args2[0], Utilities.parseInt(args2[1]));
}
}
}
if (emojiUseHistory.isEmpty()) {
String[] newRecent = new String[]{
"\uD83D\uDE02", "\uD83D\uDE18", "\u2764", "\uD83D\uDE0D", "\uD83D\uDE0A", "\uD83D\uDE01",
"\uD83D\uDC4D", "\u263A", "\uD83D\uDE14", "\uD83D\uDE04", "\uD83D\uDE2D", "\uD83D\uDC8B",
"\uD83D\uDE12", "\uD83D\uDE33", "\uD83D\uDE1C", "\uD83D\uDE48", "\uD83D\uDE09", "\uD83D\uDE03",
"\uD83D\uDE22", "\uD83D\uDE1D", "\uD83D\uDE31", "\uD83D\uDE21", "\uD83D\uDE0F", "\uD83D\uDE1E",
"\uD83D\uDE05", "\uD83D\uDE1A", "\uD83D\uDE4A", "\uD83D\uDE0C", "\uD83D\uDE00", "\uD83D\uDE0B",
"\uD83D\uDE06", "\uD83D\uDC4C", "\uD83D\uDE10", "\uD83D\uDE15"};
for (int i = 0; i < newRecent.length; i++) {
emojiUseHistory.put(newRecent[i], newRecent.length - i);
if (!preferences.getBoolean("filled_default", false)) {
String[] newRecent = new String[]{
"\uD83D\uDE02", "\uD83D\uDE18", "\u2764", "\uD83D\uDE0D", "\uD83D\uDE0A", "\uD83D\uDE01",
"\uD83D\uDC4D", "\u263A", "\uD83D\uDE14", "\uD83D\uDE04", "\uD83D\uDE2D", "\uD83D\uDC8B",
"\uD83D\uDE12", "\uD83D\uDE33", "\uD83D\uDE1C", "\uD83D\uDE48", "\uD83D\uDE09", "\uD83D\uDE03",
"\uD83D\uDE22", "\uD83D\uDE1D", "\uD83D\uDE31", "\uD83D\uDE21", "\uD83D\uDE0F", "\uD83D\uDE1E",
"\uD83D\uDE05", "\uD83D\uDE1A", "\uD83D\uDE4A", "\uD83D\uDE0C", "\uD83D\uDE00", "\uD83D\uDE0B",
"\uD83D\uDE06", "\uD83D\uDC4C", "\uD83D\uDE10", "\uD83D\uDE15"};
for (int i = 0; i < newRecent.length; i++) {
emojiUseHistory.put(newRecent[i], newRecent.length - i);
}
preferences.edit().putBoolean("filled_default", true).commit();
saveRecentEmoji();
}
saveRecentEmoji();
}
sortEmoji();
adapters.get(0).notifyDataSetChanged();
@ -1338,8 +1351,8 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
for (int a = 0; a < args.length; a++) {
String arg = args[a];
String[] args2 = arg.split("=");
Long key = Long.parseLong(args2[0]);
stickersUseHistory.put(key, Integer.parseInt(args2[1]));
Long key = Utilities.parseLong(args2[0]);
stickersUseHistory.put(key, Utilities.parseInt(args2[1]));
newRecentStickers.add(key);
}
Collections.sort(newRecentStickers, new Comparator<Long>() {
@ -1367,7 +1380,13 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
str = preferences.getString("stickers2", "");
String[] args = str.split(",");
for (int a = 0; a < args.length; a++) {
newRecentStickers.add(Long.parseLong(args[a]));
if (args[a].length() == 0) {
continue;
}
long id = Utilities.parseLong(args[a]);
if (id != 0) {
newRecentStickers.add(id);
}
}
}
sortStickers();

View File

@ -14,6 +14,7 @@ import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.FileLog;
import java.util.ArrayList;
@ -155,6 +156,7 @@ public class FrameLayoutFixed extends FrameLayout {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} catch (Exception e2) {
FileLog.e("tmessages", e2);
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(10), MeasureSpec.EXACTLY));
}
}
}

View File

@ -24,6 +24,7 @@ public class PhotoCropView extends FrameLayout {
public interface PhotoCropViewDelegate {
void needMoveImageTo(float x, float y, float s, boolean animated);
Bitmap getBitmap();
}
private boolean freeformCrop = true;
@ -38,11 +39,11 @@ public class PhotoCropView extends FrameLayout {
private float oldX = 0, oldY = 0;
private int bitmapWidth = 1, bitmapHeight = 1, bitmapX, bitmapY;
private float rectX = -1, rectY = -1;
private Bitmap bitmapToEdit;
private float bitmapGlobalScale = 1;
private float bitmapGlobalX = 0;
private float bitmapGlobalY = 0;
private PhotoCropViewDelegate delegate;
private Bitmap bitmapToEdit;
private RectF animationStartValues;
private RectF animationEndValues;
@ -472,6 +473,11 @@ public class PhotoCropView extends FrameLayout {
}
private Bitmap createBitmap(int x, int y, int w, int h) {
Bitmap newBimap = delegate.getBitmap();
if (newBimap != null) {
bitmapToEdit = newBimap;
}
Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
@ -495,6 +501,11 @@ public class PhotoCropView extends FrameLayout {
}
public Bitmap getBitmap() {
Bitmap newBimap = delegate.getBitmap();
if (newBimap != null) {
bitmapToEdit = newBimap;
}
float bitmapScaledWidth = bitmapWidth * bitmapGlobalScale;
float bitmapScaledHeight = bitmapHeight * bitmapGlobalScale;
float bitmapStartX = (getWidth() - AndroidUtilities.dp(28) - bitmapScaledWidth) / 2 + bitmapGlobalX + AndroidUtilities.dp(14);
@ -658,6 +669,11 @@ public class PhotoCropView extends FrameLayout {
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
Bitmap newBimap = delegate.getBitmap();
if (newBimap != null) {
bitmapToEdit = newBimap;
}
if (bitmapToEdit == null) {
return;
}

View File

@ -2006,7 +2006,9 @@ public class PhotoFilterView extends FrameLayout {
eglThread.postRunnable(new Runnable() {
@Override
public void run() {
eglThread.requestRender(false);
if (eglThread != null) {
eglThread.requestRender(false);
}
}
});
}

View File

@ -343,6 +343,11 @@ public class PhotoViewerCaptionEnterView extends FrameLayoutFixed implements Not
public void onStickersTab(boolean opened) {
}
@Override
public void onClearEmojiRecent() {
}
});
sizeNotifierLayout.addView(emojiView);
}

View File

@ -84,6 +84,7 @@ public class SimpleTextView extends View {
} else {
offsetX = 0;
}
offsetX += getPaddingLeft();
}
} catch (Exception e) {
//ignore
@ -94,7 +95,7 @@ public class SimpleTextView extends View {
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (changed) {
createLayout(right - left);
createLayout(right - left - getPaddingLeft() - getPaddingRight());
invalidate();
wasLayout = true;
}
@ -103,7 +104,7 @@ public class SimpleTextView extends View {
public void setText(CharSequence value) {
text = value;
if (wasLayout) {
createLayout(getMeasuredWidth());
createLayout(getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
invalidate();
} else {
requestLayout();

View File

@ -391,6 +391,7 @@ public class Switch extends CompoundButton {
protected void onAttachedToWindow() {
super.onAttachedToWindow();
attachedToWindow = true;
requestLayout();
}
@Override

View File

@ -40,6 +40,7 @@ import org.telegram.messenger.ApplicationLoader;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.MessagesStorage;
import org.telegram.messenger.SecretChatHelper;
import org.telegram.messenger.UserConfig;
import org.telegram.messenger.UserObject;
import org.telegram.tgnet.TLRPC;
import org.telegram.messenger.ContactsController;
@ -289,12 +290,17 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter
didSelectResult(user, true, null);
} else {
if (createSecretChat) {
if (user.id == UserConfig.getClientUserId()) {
return;
}
creatingChat = true;
SecretChatHelper.getInstance().startSecretChat(getParentActivity(), user);
} else {
Bundle args = new Bundle();
args.putInt("user_id", user.id);
presentFragment(new ChatActivity(args), true);
if (MessagesController.checkCanOpenChat(args, ContactsActivity.this)) {
presentFragment(new ChatActivity(args), true);
}
}
}
} else {
@ -330,6 +336,7 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter
args.putBoolean("onlyUsers", true);
args.putBoolean("destroyAfterSelect", true);
args.putBoolean("createSecretChat", true);
args.putBoolean("allowBots", false);
presentFragment(new ContactsActivity(args), false);
} else if (row == 2) {
if (!MessagesController.isFeatureEnabled("broadcast_create", ContactsActivity.this)) {
@ -363,7 +370,9 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter
} else {
Bundle args = new Bundle();
args.putInt("user_id", user.id);
presentFragment(new ChatActivity(args), true);
if (MessagesController.checkCanOpenChat(args, ContactsActivity.this)) {
presentFragment(new ChatActivity(args), true);
}
}
}
} else if (item instanceof ContactsController.Contact) {

View File

@ -0,0 +1,216 @@
/*
* This is the source code of Telegram for Android v. 3.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2016.
*/
package org.telegram.ui;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.FrameLayout;
import android.widget.ListView;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.MessagesController;
import org.telegram.messenger.NotificationCenter;
import org.telegram.messenger.R;
import org.telegram.ui.ActionBar.ActionBar;
import org.telegram.ui.ActionBar.BaseFragment;
import org.telegram.ui.Adapters.BaseFragmentAdapter;
import org.telegram.ui.Cells.TextInfoPrivacyCell;
import org.telegram.ui.Cells.TextSettingsCell;
import org.telegram.ui.Components.LayoutHelper;
public class ConvertGroupActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate {
private ListAdapter listAdapter;
private int convertInfoRow;
private int convertRow;
private int convertDetailRow;
private int rowCount;
private int chat_id;
public ConvertGroupActivity(Bundle args) {
super(args);
chat_id = args.getInt("chat_id");
}
@Override
public boolean onFragmentCreate() {
super.onFragmentCreate();
convertInfoRow = rowCount++;
convertRow = rowCount++;
convertDetailRow = rowCount++;
NotificationCenter.getInstance().addObserver(this, NotificationCenter.closeChats);
return true;
}
@Override
public void onFragmentDestroy() {
super.onFragmentDestroy();
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.closeChats);
}
@Override
public View createView(Context context) {
actionBar.setBackButtonImage(R.drawable.ic_ab_back);
actionBar.setAllowOverlayTitle(true);
actionBar.setTitle(LocaleController.getString("ConvertGroup", R.string.ConvertGroup));
actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() {
@Override
public void onItemClick(int id) {
if (id == -1) {
finishFragment();
}
}
});
listAdapter = new ListAdapter(context);
fragmentView = new FrameLayout(context);
FrameLayout frameLayout = (FrameLayout) fragmentView;
frameLayout.setBackgroundColor(0xfff0f0f0);
ListView listView = new ListView(context);
listView.setDivider(null);
listView.setDividerHeight(0);
listView.setVerticalScrollBarEnabled(false);
listView.setDrawSelectorOnTop(true);
frameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
listView.setAdapter(listAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(final AdapterView<?> adapterView, View view, final int i, long l) {
if (i == convertRow) {
AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity());
builder.setMessage(LocaleController.getString("ConvertGroupAlert", R.string.ConvertGroupAlert));
builder.setTitle(LocaleController.getString("ConvertGroupAlertWarning", R.string.ConvertGroupAlertWarning));
builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
MessagesController.getInstance().convertToMegaGroup(getParentActivity(), chat_id);
}
});
builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null);
showDialog(builder.create());
}
}
});
return fragmentView;
}
@Override
public void onResume() {
super.onResume();
if (listAdapter != null) {
listAdapter.notifyDataSetChanged();
}
}
@Override
public void didReceivedNotification(int id, Object... args) {
if (id == NotificationCenter.closeChats) {
removeSelfFromStack();
}
}
private class ListAdapter extends BaseFragmentAdapter {
private Context mContext;
public ListAdapter(Context context) {
mContext = context;
}
@Override
public boolean areAllItemsEnabled() {
return false;
}
@Override
public boolean isEnabled(int i) {
return i == convertRow;
}
@Override
public int getCount() {
return rowCount;
}
@Override
public Object getItem(int i) {
return null;
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public boolean hasStableIds() {
return false;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
int type = getItemViewType(i);
if (type == 0) {
if (view == null) {
view = new TextSettingsCell(mContext);
view.setBackgroundColor(0xffffffff);
}
TextSettingsCell textCell = (TextSettingsCell) view;
if (i == convertRow) {
textCell.setText(LocaleController.getString("ConvertGroup", R.string.ConvertGroup), false);
}
} else if (type == 1) {
if (view == null) {
view = new TextInfoPrivacyCell(mContext);
}
if (i == convertInfoRow) {
((TextInfoPrivacyCell) view).setText(AndroidUtilities.replaceTags(LocaleController.getString("ConvertGroupInfo2", R.string.ConvertGroupInfo2)));
view.setBackgroundResource(R.drawable.greydivider);
} else if (i == convertDetailRow) {
((TextInfoPrivacyCell) view).setText(AndroidUtilities.replaceTags(LocaleController.getString("ConvertGroupInfo3", R.string.ConvertGroupInfo3)));
view.setBackgroundResource(R.drawable.greydivider_bottom);
}
}
return view;
}
@Override
public int getItemViewType(int i) {
if (i == convertRow) {
return 0;
} else if (i == convertInfoRow || i == convertDetailRow) {
return 1;
}
return 0;
}
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public boolean isEmpty() {
return false;
}
}
}

View File

@ -147,6 +147,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter.
NotificationCenter.getInstance().addObserver(this, NotificationCenter.messageSendError);
NotificationCenter.getInstance().addObserver(this, NotificationCenter.didSetPasscode);
NotificationCenter.getInstance().addObserver(this, NotificationCenter.needReloadRecentDialogsSearch);
NotificationCenter.getInstance().addObserver(this, NotificationCenter.didLoadedReplyMessages);
}
@ -175,6 +176,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter.
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.messageSendError);
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.didSetPasscode);
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.needReloadRecentDialogsSearch);
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.didLoadedReplyMessages);
}
delegate = null;
}
@ -429,10 +431,14 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter.
}
}
if (searchString != null) {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.closeChats);
presentFragment(new ChatActivity(args));
if (MessagesController.checkCanOpenChat(args, DialogsActivity.this)) {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.closeChats);
presentFragment(new ChatActivity(args));
}
} else {
presentFragment(new ChatActivity(args));
if (MessagesController.checkCanOpenChat(args, DialogsActivity.this)) {
presentFragment(new ChatActivity(args));
}
}
}
}
@ -896,9 +902,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter.
}
}
} else if (id == NotificationCenter.emojiDidLoaded) {
if (listView != null) {
updateVisibleRows(0);
}
updateVisibleRows(0);
} else if (id == NotificationCenter.updateInterfaces) {
updateVisibleRows((Integer) args[0]);
} else if (id == NotificationCenter.appDidLogout) {
@ -933,6 +937,8 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter.
if (dialogsSearchAdapter != null) {
dialogsSearchAdapter.loadRecentSearch();
}
} else if (id == NotificationCenter.didLoadedReplyMessages) {
updateVisibleRows(0);
}
}

View File

@ -78,7 +78,7 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen
private int beforeChangeIndex;
private boolean ignoreChange;
private CharSequence changeString;
private int maxCount = 1000;
private int maxCount = 5000;
private int chatType = ChatObject.CHAT_TYPE_CHAT;
private boolean isAlwaysShare;
private boolean isNeverShare;

View File

@ -450,7 +450,7 @@ public class GroupCreateFinalActivity extends BaseFragment implements Notificati
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
if (view == null) {
view = new UserCell(mContext, 1, 0);
view = new UserCell(mContext, 1, 0, false);
}
TLRPC.User user = MessagesController.getInstance().getUser(selectedContacts.get(i));

View File

@ -127,7 +127,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa
return;
}
if (intent != null && !intent.getBooleanExtra("fromIntro", false)) {
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("logininfo", MODE_PRIVATE);
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("logininfo2", MODE_PRIVATE);
Map<String, ?> state = preferences.getAll();
if (state.isEmpty()) {
Intent intent2 = new Intent(this, IntroActivity.class);
@ -295,6 +295,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa
args.putBoolean("onlyUsers", true);
args.putBoolean("destroyAfterSelect", true);
args.putBoolean("createSecretChat", true);
args.putBoolean("allowBots", false);
presentFragment(new ContactsActivity(args));
drawerLayoutContainer.closeDrawer(false);
} else if (position == 4) {
@ -768,7 +769,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa
if (scheme != null) {
if ((scheme.equals("http") || scheme.equals("https"))) {
String host = data.getHost().toLowerCase();
if (host.equals("telegram.me")) {
if (host.equals("telegram.me") || host.equals("telegram.dog")) {
String path = data.getPath();
if (path != null && path.length() > 1) {
path = path.substring(1);
@ -882,16 +883,20 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa
if (push_user_id != 0) {
Bundle args = new Bundle();
args.putInt("user_id", push_user_id);
ChatActivity fragment = new ChatActivity(args);
if (actionBarLayout.presentFragment(fragment, false, true, true)) {
pushOpened = true;
if (mainFragmentsStack.isEmpty() || MessagesController.checkCanOpenChat(args, mainFragmentsStack.get(mainFragmentsStack.size() - 1))) {
ChatActivity fragment = new ChatActivity(args);
if (actionBarLayout.presentFragment(fragment, false, true, true)) {
pushOpened = true;
}
}
} else if (push_chat_id != 0) {
Bundle args = new Bundle();
args.putInt("chat_id", push_chat_id);
ChatActivity fragment = new ChatActivity(args);
if (actionBarLayout.presentFragment(fragment, false, true, true)) {
pushOpened = true;
if (mainFragmentsStack.isEmpty() || MessagesController.checkCanOpenChat(args, mainFragmentsStack.get(mainFragmentsStack.size() - 1))) {
ChatActivity fragment = new ChatActivity(args);
if (actionBarLayout.presentFragment(fragment, false, true, true)) {
pushOpened = true;
}
}
} else if (push_enc_id != 0) {
Bundle args = new Bundle();
@ -1071,12 +1076,14 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa
fragment.setDelegate(new DialogsActivity.MessagesActivityDelegate() {
@Override
public void didSelectDialog(DialogsActivity fragment, long did, boolean param) {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.closeChats);
MessagesController.getInstance().addUserToChat(-(int) did, user, null, 0, botChat, null);
Bundle args = new Bundle();
args.putBoolean("scrollToTopOnResume", true);
args.putInt("chat_id", -(int) did);
actionBarLayout.presentFragment(new ChatActivity(args), true, false, true);
if (mainFragmentsStack.isEmpty() || MessagesController.checkCanOpenChat(args, mainFragmentsStack.get(mainFragmentsStack.size() - 1))) {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.closeChats);
MessagesController.getInstance().addUserToChat(-(int) did, user, null, 0, botChat, null);
actionBarLayout.presentFragment(new ChatActivity(args), true, false, true);
}
}
});
presentFragment(fragment);
@ -1093,9 +1100,11 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa
if (messageId != null) {
args.putInt("message_id", messageId);
}
ChatActivity fragment = new ChatActivity(args);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.closeChats);
actionBarLayout.presentFragment(fragment, false, true, true);
if (mainFragmentsStack.isEmpty() || MessagesController.checkCanOpenChat(args, mainFragmentsStack.get(mainFragmentsStack.size() - 1))) {
ChatActivity fragment = new ChatActivity(args);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.closeChats);
actionBarLayout.presentFragment(fragment, false, true, true);
}
}
} else {
try {
@ -1134,9 +1143,11 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa
MessagesStorage.getInstance().putUsersAndChats(null, chats, false, true);
Bundle args = new Bundle();
args.putInt("chat_id", invite.chat.id);
ChatActivity fragment = new ChatActivity(args);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.closeChats);
actionBarLayout.presentFragment(fragment, false, true, true);
if (mainFragmentsStack.isEmpty() || MessagesController.checkCanOpenChat(args, mainFragmentsStack.get(mainFragmentsStack.size() - 1))) {
ChatActivity fragment = new ChatActivity(args);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.closeChats);
actionBarLayout.presentFragment(fragment, false, true, true);
}
} else {
AlertDialog.Builder builder = new AlertDialog.Builder(LaunchActivity.this);
builder.setTitle(LocaleController.getString("AppName", R.string.AppName));
@ -1200,9 +1211,11 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa
MessagesController.getInstance().putChats(updates.chats, false);
Bundle args = new Bundle();
args.putInt("chat_id", chat.id);
ChatActivity fragment = new ChatActivity(args);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.closeChats);
actionBarLayout.presentFragment(fragment, false, true, true);
if (mainFragmentsStack.isEmpty() || MessagesController.checkCanOpenChat(args, mainFragmentsStack.get(mainFragmentsStack.size() - 1))) {
ChatActivity fragment = new ChatActivity(args);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.closeChats);
actionBarLayout.presentFragment(fragment, false, true, true);
}
}
}
} else {
@ -1238,11 +1251,6 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa
fragment.setDelegate(new DialogsActivity.MessagesActivityDelegate() {
@Override
public void didSelectDialog(DialogsActivity fragment, long did, boolean param) {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.closeChats);
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putString("dialog_" + did, message);
editor.commit();
Bundle args = new Bundle();
args.putBoolean("scrollToTopOnResume", true);
args.putBoolean("hasUrl", hasUrl);
@ -1261,7 +1269,14 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa
} else {
args.putInt("enc_id", high_id);
}
actionBarLayout.presentFragment(new ChatActivity(args), true, false, true);
if (MessagesController.checkCanOpenChat(args, fragment)) {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.closeChats);
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putString("dialog_" + did, message);
editor.commit();
actionBarLayout.presentFragment(new ChatActivity(args), true, false, true);
}
}
});
presentFragment(fragment, false, true);
@ -1339,6 +1354,9 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa
} else {
args.putInt("enc_id", high_id);
}
if (!MessagesController.checkCanOpenChat(args, dialogsFragment)) {
return;
}
ChatActivity fragment = new ChatActivity(args);
if (videoPath != null) {
@ -1495,7 +1513,12 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa
actionBarLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
needLayout();
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
needLayout();
}
});
if (actionBarLayout != null) {
if (Build.VERSION.SDK_INT < 16) {
actionBarLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);

View File

@ -929,7 +929,11 @@ public class LocationActivity extends BaseFragment implements NotificationCenter
super.onResume();
AndroidUtilities.removeAdjustResize(getParentActivity(), classGuid);
if (mapView != null) {
mapView.onResume();
try {
mapView.onResume();
} catch (Throwable e) {
FileLog.e("tmessages", e);
}
}
updateUserData();
fixLayoutInternal(true);

View File

@ -348,6 +348,9 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No
} else if (lower_part < 0) {
args.putInt("chat_id", -lower_part);
}
if (!MessagesController.checkCanOpenChat(args, fragment)) {
return;
}
ArrayList<MessageObject> fmessages = new ArrayList<>();
for (int a = 1; a >= 0; a--) {
@ -364,6 +367,7 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No
actionBar.hideActionMode();
NotificationCenter.getInstance().postNotificationName(NotificationCenter.closeChats);
ChatActivity chatActivity = new ChatActivity(args);
presentFragment(chatActivity, true);
chatActivity.showReplyPanel(true, null, fmessages, null, false, false);
@ -901,7 +905,11 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No
listView.setAdapter(photoVideoAdapter);
dropDown.setText(LocaleController.getString("SharedMediaTitle", R.string.SharedMediaTitle));
emptyImageView.setImageResource(R.drawable.tip1);
emptyTextView.setText(LocaleController.getString("NoMedia", R.string.NoMedia));
if ((int) dialog_id == 0) {
emptyTextView.setText(LocaleController.getString("NoMediaSecret", R.string.NoMediaSecret));
} else {
emptyTextView.setText(LocaleController.getString("NoMedia", R.string.NoMedia));
}
searchItem.setVisibility(View.GONE);
if (sharedMediaData[selectedMode].loading && sharedMediaData[selectedMode].messages.isEmpty()) {
progressView.setVisibility(View.VISIBLE);
@ -918,12 +926,20 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No
listView.setAdapter(documentsAdapter);
dropDown.setText(LocaleController.getString("DocumentsTitle", R.string.DocumentsTitle));
emptyImageView.setImageResource(R.drawable.tip2);
emptyTextView.setText(LocaleController.getString("NoSharedFiles", R.string.NoSharedFiles));
if ((int) dialog_id == 0) {
emptyTextView.setText(LocaleController.getString("NoSharedFilesSecret", R.string.NoSharedFilesSecret));
} else {
emptyTextView.setText(LocaleController.getString("NoSharedFiles", R.string.NoSharedFiles));
}
} else if (selectedMode == 4) {
listView.setAdapter(audioAdapter);
dropDown.setText(LocaleController.getString("AudioTitle", R.string.AudioTitle));
emptyImageView.setImageResource(R.drawable.tip4);
emptyTextView.setText(LocaleController.getString("NoSharedAudio", R.string.NoSharedAudio));
if ((int) dialog_id == 0) {
emptyTextView.setText(LocaleController.getString("NoSharedAudioSecret", R.string.NoSharedAudioSecret));
} else {
emptyTextView.setText(LocaleController.getString("NoSharedAudio", R.string.NoSharedAudio));
}
}
searchItem.setVisibility(!sharedMediaData[selectedMode].messages.isEmpty() ? View.VISIBLE : View.GONE);
if (!sharedMediaData[selectedMode].loading && !sharedMediaData[selectedMode].endReached[0] && sharedMediaData[selectedMode].messages.isEmpty()) {
@ -944,7 +960,11 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No
listView.setAdapter(linksAdapter);
dropDown.setText(LocaleController.getString("LinksTitle", R.string.LinksTitle));
emptyImageView.setImageResource(R.drawable.tip3);
emptyTextView.setText(LocaleController.getString("NoSharedLinks", R.string.NoSharedLinks));
if ((int) dialog_id == 0) {
emptyTextView.setText(LocaleController.getString("NoSharedLinksSecret", R.string.NoSharedLinksSecret));
} else {
emptyTextView.setText(LocaleController.getString("NoSharedLinks", R.string.NoSharedLinks));
}
searchItem.setVisibility(!sharedMediaData[3].messages.isEmpty() ? View.VISIBLE : View.GONE);
if (!sharedMediaData[selectedMode].loading && !sharedMediaData[selectedMode].endReached[0] && sharedMediaData[selectedMode].messages.isEmpty()) {
sharedMediaData[selectedMode].loading = true;

View File

@ -15,7 +15,6 @@ import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
@ -58,16 +57,6 @@ public class PhotoCropActivity extends BaseFragment {
init();
}
public PhotoCropView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public PhotoCropView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
rectPaint = new Paint();
rectPaint.setColor(0x3ffafafa);

View File

@ -914,6 +914,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat
};
windowView.setBackgroundDrawable(backgroundDrawable);
windowView.setFocusable(false);
if (Build.VERSION.SDK_INT >= 23) {
windowView.setFitsSystemWindows(true); //TODO ?
}
animatingImageView = new ClippingImageView(activity);
animatingImageView.setAnimationValues(animationValues);
@ -1206,10 +1209,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat
if (currentMessageObject != null) {
isVideo = currentMessageObject.isVideo();
if (currentMessageObject.messageOwner.media instanceof TLRPC.TL_messageMediaWebPage) {
/*if (currentMessageObject.messageOwner.media instanceof TLRPC.TL_messageMediaWebPage) {
AndroidUtilities.openUrl(parentActivity, currentMessageObject.messageOwner.media.webpage.url);
return;
}
}*/
f = FileLoader.getPathToMessage(currentMessageObject.messageOwner);
} else if (currentFileLocation != null) {
f = FileLoader.getPathToAttach(currentFileLocation, avatarsUserId != 0);
@ -1800,6 +1803,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat
containerView.invalidate();
}
}
@Override
public Bitmap getBitmap() {
return centerImage.getBitmap();
}
});
}
@ -2579,7 +2587,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat
captionTextViewNew = captionTextView;
captionItem.setIcon(R.drawable.photo_text2);
CharSequence str = Emoji.replaceEmoji(new SpannableStringBuilder(caption.toString()), MessageObject.textPaint.getFontMetricsInt(), AndroidUtilities.dp(20), false);
CharSequence str = Emoji.replaceEmoji(new SpannableStringBuilder(caption.toString()), MessageObject.getTextPaint().getFontMetricsInt(), AndroidUtilities.dp(20), false);
captionTextView.setTag(str);
captionTextView.setText(str);
ViewProxy.setAlpha(captionTextView, bottomLayout.getVisibility() == View.VISIBLE || pickerView.getVisibility() == View.VISIBLE ? 1.0f : 0.0f);
@ -2973,11 +2981,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat
}
});
}
@Override
public void onAnimationCancel(Object animation) {
onAnimationEnd(animation);
}
});
transitionAnimationStartTime = System.currentTimeMillis();
AndroidUtilities.runOnUIThread(new Runnable() {
@ -3178,11 +3181,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat
}
});
}
@Override
public void onAnimationCancel(Object animation) {
onAnimationEnd(animation);
}
});
transitionAnimationStartTime = System.currentTimeMillis();
if (Build.VERSION.SDK_INT >= 18) {

View File

@ -285,7 +285,7 @@ public class PrivacyUsersActivity extends BaseFragment implements NotificationCe
int type = getItemViewType(i);
if (type == 0) {
if (view == null) {
view = new UserCell(mContext, 1, 0);
view = new UserCell(mContext, 1, 0, false);
}
TLRPC.User user = MessagesController.getInstance().getUser(uidArray.get(i));
((UserCell)view).setData(user, null, user.phone != null && user.phone.length() != 0 ? PhoneFormat.getInstance().format("+" + user.phone) : LocaleController.getString("NumberUnknown", R.string.NumberUnknown), 0);

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